タブレット用プログラムの書き止め

android OS & iPadOS の記録。

基礎。OverScroller 【 GestureDetectorCompat 、ImageSwitcher と OverScroller 】

2022-01-13 23:27:19 | Android studio 日記

スクローラーは苦しんだ。今もよく分からない。とりあえず、動く。

スクロールは、スクロール範囲を考える事。
スクリーンサイズが、200x100。
画像が400x200。だったとすると、
画像の(0,0)から(200,100)まで。x= 0~200、y= 0~100。
このx、yの組み合わせで元画像がピッタリとスクリーン上をスクロールできる。

スクリーン
sW = 200
sH = 100

画像
iW = 400
iH = 200

scrollMax
sMaxX = iW - sW
sMaxY = iH - sH

0 <= scrX <= sMaxX
0 <= scrY <= sMaxY
スライドの移動量を計算してスクロール。

画像が小さかったら、scrollMaxは負になる。
その時はスクロールさせない。

onScroll()は、その制限でスクロールさせる。
imageView に padding 設定をするとスクロールオフセットは切り離せる。

scrW=200/ imageW=100 の時、view.setPadding( 50, paddingY, 50, paddingY);
と設定すると、view.scrollTo( 0, y );は画面中央、両端空白あり。
view.setPadding( 0, 0, 0, 0 );(未設定状態のつもり)だと
view.scrollTo( 0, y );は画面左端寄り、右側空白あり。
状況により使い分け。

スクロール前提なら画像をセットした時に padding も設定かな。


面倒なのがフリンク。

デベロッパーのリファレンスが分からん。文献も分からん。
思いつくことを試して結果データを分析して多分?こうかな?

・スクローラーとアニメーターを定義。
・アニメーターを実行。(別スレッドで動いているみたい)
・アニメーター内でスクローラーに計算させて、値を取り出し、view.scrollTo( x, y )にセット。
・スクローラーが完了したら、アニメーターを止める。

みたいな。

mScroller.fling(
    startX,
    startY,
    velocityX,
    velocityY,
    minX, maxX,
    minY, maxY,
    overX,overY
    );


【startX, startY】
タップ位置だと思うよね~。これをDown位置と捉えた時はスクロール値に変換をしないといけない。
最初からスクロールオフセットだと決めれば、後の min、max はスクロールの制限範囲になる。計算もすこぶる楽。
ただし、制限が付く。フルスクリーンフルスクロールのみの設定。
複数のコンテンツが合わさって複数の領域でスクロールさせる場合は使えない。と思う。注意!

自分はフルスクリーンフルスクロールだから楽にいく。Padding設定は必須。
検索での説明は指定領域内の狭い範囲のスクロールばかりだったので、理解に苦しんだ。orz...

別問題も残っている。OverScroll の overX,overY の設定は、画像外にスクロールが飛び出して、正常な位置に戻るという飛び出し距離。
試したが時々、完全に戻らない。調べると本来スクローラーが完了してアニメーターが終了するはずが、スクローラーが完了の信号を受け取らずにアニメーターがうやむやにしている事が時々ある。
謎だ?別ルートで何か動いている…。なのでオーバー仕様は封印。


public class MainActivity extends AppCompatActivity {

    private GestureDetectorCompat mDetector;

    private OverScroller mScroller;
    private ValueAnimator scrollAnimator = ValueAnimator.ofFloat(0,1);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mDetector = new GestureDetectorCompat(this, new MyGestureListener() );

        mScroller = new OverScroller( this,null );
        scrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) { // アニメーター実行で呼ばれる
                if ( !mScroller.isFinished() ) {
                    mScroller.computeScrollOffset(); // 計算
                    mImageSwitcher.getCurrentView().scrollTo( mScroller.getCurrX(), mScroller.getCurrY() ); // スクロール実行
                } else
                    scrollAnimator.cancel();
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if ( mDetector.onTouchEvent(event) ) return true;
        return super.onTouchEvent(event);
    }

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

        @ Override
        public boolean onDown(MotionEvent event) {
            if ( !mScroller.isFinished() ) mScroller.abortAnimation();
            scrollAnimator.cancel();
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY) {
            scrollImage( (ImageView)mImageSwitcher.getCurrentView(), distanceX, distanceY );
            return true;
        }

        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
            ImageView view = (ImageView)mImageSwitcher.getCurrentView();
            int w = view.getDrawable().getIntrinsicWidth();
            int h = view.getDrawable().getIntrinsicHeight();

            int startX = view.getScrollX();
            int startY = view.getScrollY();

            mScroller.fling(
                    startX,
                    startY,
                    (int) -velocityX,
                    (int) -velocityY,
                    0, Math.max(0,w-getScreenWidth()),
                    0, Math.max(0,h-getScreenHeight())
            );
            scrollAnimator.start(); // 実行
            return true;
        }
        @Override
        public boolean onSingleTapConfirmed(MotionEvent event) {
            loadImage((int)event.getY());
            return true;
        }
    };

    public void scrollImage ( ImageView view, float x, float y ) {

        int cX = (int) x;
        int cY = (int) y;
        int scrX = view.getScrollX();
        int scrY = view.getScrollY();
        int dx = ( view.getDrawable().getIntrinsicWidth() - getScreenWidth() );
        int dy = ( view.getDrawable().getIntrinsicHeight() - getScreenHeight() );
        if ( dx > 0 ) {
            if ( x > 0 ) {
                if ( scrX == dx ) cX = 0;
                else {
                    int xx = dx - scrX;
                    if ( x >= xx ) cX = xx;
                }
            } else if ( x < 0 ){
                if ( scrX == 0 ) cX = 0;
                else if ( scrX > 0 && -scrX >= x ) cX = -scrX;
            } else cX = 0;
        } else
            cX = 0;

        if ( dy > 0 ) {
            if ( y > 0 ) {
                if ( scrY == dy ) cY = 0;
                else {
                    int yy = dy - scrY;
                    if ( y >= yy ) cY = yy;
                }
            } else if ( y < 0 ){
                if ( scrY == 0 ) cY = 0;
                else if ( scrY > 0 && -scrY >= y ) cY = -scrY;
            } else cY = 0;
        } else
            cY = 0;

        view.scrollBy( cX, cY );
    }

}

 


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

基礎。ImageSwitcher 【 GestureDetectorCompat 、ImageSwitcher と OverScroller 】

2022-01-13 23:14:05 | Android studio 日記

次にImageSwitcher について。
Bitmap を bitmapDrawable に変換して、mImageSwitcherにセットするだけ。
次にセットした画像を前に表示しているものとアニメーションで交換してくれる。(検索:AnimationUtils)

ImageView view;
view = (ImageView)mImageSwitcher.getCurrentView(); // 表示View
view = (ImageView)mImageSwitcher.getNextView()     // 待機View

int imageWidth = view.getDrawable().getIntrinsicWidth();
int scrollOffsetX = view.getScrollX();

何となく、セットした画像サイズ、オフセットが取り出せる。
imageView.setScaleType() でフィット表示させていて、スクロール処理に入っちゃうとまずいことになる。
フィットモードの時はスクロールをはじくこと。
view.getDrawable().getIntrinsicWidth()は元画像のサイズを返すから正常にスクロールしない。

 

public class MainActivity extends AppCompatActivity {
    private ImageSwitcher mImageSwitcher = null;
    private AnimationSet mAnimationSet;

    private ImageView.ScaleType imageViewScaleType = ImageView.ScaleType.MATRIX;

    private BitmapDrawable bitmapDrawable;
    private Bitmap mBitmap = null, mBitmap0 = null;
    private boolean flag = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageSwitcher = findViewById(R.id.image_switcher);

        mImageSwitcher.setFactory(new ViewSwitcher.ViewFactory() {
            @Override
            public View makeView() {
                ImageView imageView = new ImageView(getApplicationContext());
                imageView.setScaleType(imageViewScaleType);
                return imageView;
            }
        });

        setNextAnimation();

        File f = getExternalStoragePublicDirectory(DIRECTORY_PICTURES);
        File f1 = new File(f.getAbsolutePath() + "/image1/182.jpg");
        File f2 = new File(f.getAbsolutePath() + "/image1/1.jpg");

        mBitmap0 = BitmapFactory.decodeFile(f1.getAbsolutePath());
        mBitmap = BitmapFactory.decodeFile(f2.getAbsolutePath());

        bitmapDrawable = new BitmapDrawable( getResources(),mBitmap );
        mImageSwitcher.setImageDrawable(bitmapDrawable);

        setPaddingImageView( (ImageView) mImageSwitcher.getNextView(), mBitmap.getWidth(), mBitmap.getHeight() );
    }

    public void loadImage(int y) { // タッチイベントで呼べば、切り替えをしてくれる。今回省略。

        if ( y < 400 ) setNextAnimation();
        else setPrevAnimation();

        if (flag) {
            setPaddingImageView( (ImageView) mImageSwitcher.getNextView(), mBitmap0.getWidth(), mBitmap0.getHeight() );
            mImageSwitcher.setImageDrawable(new BitmapDrawable(getResources(), mBitmap0));
        } else {
            setPaddingImageView( (ImageView) mImageSwitcher.getNextView(), mBitmap.getWidth(), mBitmap.getHeight() );
            mImageSwitcher.setImageDrawable(new BitmapDrawable(getResources(), mBitmap));
        }
        flag = !flag;
        mImageSwitcher.getCurrentView().scrollTo( 0,0 );
    }

    private void setNextAnimation() { // resフォルダ内に anim フォルダを作り、next_in_animation_set.xml ファイルを作る。
        mImageSwitcher.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.next_in_animation_set));
        mImageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.next_out_animation_set));
    }

    private void setPrevAnimation() {
        mImageSwitcher.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.prev_in_animation_set));
        mImageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.prev_out_animation_set));
    }


    public void setPaddingImageView(ImageView view, int bx, int by) {

        int screenWidth = getScreenWidth();
        int screenHeight = getScreenHeight();
        int paddingX, paddingY;
        if (bx < screenWidth) {
            paddingX = (screenWidth - bx) / 2;
        } else paddingX = 0;
        if (by < screenHeight) {
            paddingY = (screenHeight - by) / 2;
        } else paddingY = 0;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            view.setPaddingRelative(paddingX, paddingY, paddingX, paddingY);
        } else
            view.setPadding(paddingX, paddingY, paddingX, paddingY);
    }

    public int getScreenWidth() {
        WindowManager wManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            WindowMetrics mWindowMetrics = wManager.getMaximumWindowMetrics();
            return mWindowMetrics.getBounds().width();
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            DisplayMetrics mDisplayMetrics = new DisplayMetrics();
            wManager.getDefaultDisplay().getRealMetrics(mDisplayMetrics);
            return mDisplayMetrics.widthPixels;
        }
        Point realSize = new Point();
        wManager.getDefaultDisplay().getSize(realSize);
        return realSize.x;
    }

    public int getScreenHeight() {
        WindowManager wManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            WindowMetrics mWindowMetrics = wManager.getCurrentWindowMetrics();
            return mWindowMetrics.getBounds().height();
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            DisplayMetrics mDisplayMetrics = new DisplayMetrics();
            wManager.getDefaultDisplay().getRealMetrics(mDisplayMetrics);
            return mDisplayMetrics.heightPixels;
        }
        Point realSize = new Point();
        wManager.getDefaultDisplay().getSize(realSize);
        return realSize.y;
    }

}

【next_in_animation_set.xml】下から上に移動指示。

< ?xml version="1.0" encoding="utf-8"?>
< set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="@android:anim/decelerate_interpolator">

    < translate
        android:fromYDelta="100%"
        android:toYDelta="0%"
        android:fillAfter="true"
        android:duration="250" />

< /set>

他は省略。


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

基礎。GestureDetectorCompat 【 GestureDetectorCompat 、ImageSwitcher と OverScroller 】

2022-01-13 22:52:50 | Android studio 日記

まず、ジェスチャーのおさらい。
onTouchEvent()からのフックとオーバーライド。
タップ処理をここで完結させるには、true を返す。

public class MainActivity extends AppCompatActivity {

    private GestureDetectorCompat mDetector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mDetector = new GestureDetectorCompat(this, new MyGestureListener() );

    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if ( mDetector.onTouchEvent(event) ) return true;
        return super.onTouchEvent(event);
    }


    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

        @ Override
        public boolean onDown(MotionEvent event) {
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY) {
            return true;
        }

        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
            return true;
        }
        @Override
        public boolean onSingleTapConfirmed(MotionEvent event) {
            return true;
        }

    };

}

目標のビューの onTouchEvent() に Detector.onTouchEvent() を寄生させタップ処理をさせる。
ScrollViewの重ね掛け以外は普通に使えるんだけど。

 

3部構成の一部部分は省略掲載になっている。だが、プログラムを全部組み合わせると、とりあえずテストできる状態になる。プロジェクトの設定状態では問題が起こるかもしれないが…。

フリンクしてスクロールさせる時は滑ってパンさせる事を推奨しているらしいので組み込んだ。

 


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする