自定義控制元件ViewPager

weixin_33978044發表於2018-10-18

效果圖:


11863487-5f5c5894ae69d96b.png
image.png

MainActivity

package com.example.administrator.viewgroup_xianxi;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
//scrollToPager(id);
public class MainActivity extends Activity {
    private MyviewGroup ViewGroupText;

int dis[]={R.drawable.kkkk, R.drawable.kkkkkk,R.drawable.aaaa};
    private RadioGroup rgMain;




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewGroupText = (MyviewGroup) findViewById(R.id.ViewGroup_text);
        rgMain = (RadioGroup) findViewById(R.id.rg_main);

        for (int i = 0;  i < dis.length; i++) {
            ImageView imageView = new ImageView(this);
            imageView.setBackgroundResource(dis[i]);
            ViewGroupText.addView(imageView);
        }
        //因為載入的是一個佈局所以佈局裡面的控制元件要進行測量大小
        View addb=View.inflate(this,R.layout.addb,null);
        ViewGroupText.addView(addb,1);

        for (int i = 0; i < ViewGroupText.getChildCount(); i++) {
            RadioButton button=new RadioButton(this);
            button.setId(i);//0-3id
            if(i==0){
                button.setChecked(true);
            }
            rgMain.addView(button);
        }

        //設定RadioGroup選中的變化
        rgMain.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int id) {
                //MyviewPager
                ViewGroupText.scrollToPager(id);//根據下表定位到具體頁面;罪上面
            }
        });
        ViewGroupText.setOnpagerChanglistenter(new MyviewGroup.OnpagerChanglistenter() {
            @Override
            public void onScrollToPager(int position) {
                rgMain.check(position);
            }
        });

    }

}

main佈局.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:cheng="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <RadioGroup
        android:id="@+id/rg_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="horizontal"></RadioGroup>

    <com.example.administrator.viewgroup_xianxi.MyviewGroup
        android:id="@+id/ViewGroup_text"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </com.example.administrator.viewgroup_xianxi.MyviewGroup>
</LinearLayout>

自定義MyviewGroup類

package com.example.administrator.viewgroup_xianxi;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
import android.widget.Toast;

public class MyviewGroup extends ViewGroup {
    int currentIndx;//當前的座標
    private Scroller mScrolier;
    /**
     * 手勢介面卡
     * 1,定義出來
     * 2,例項化-把想要的方法給重新
     * 3,在ontouchEvent()把事件鑽地給手勢識別器
     *
     */
    private GestureDetector mGestureDetector;


    public MyviewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    private void initView(final Context context) {
        mScrolier=new Scroller(context);
        //2,例項化-把想要的方法給重新
        mGestureDetector=new GestureDetector(context,new GestureDetector.SimpleOnGestureListener(){
            @Override
            public void onLongPress(MotionEvent e) {
                Toast.makeText(context, "長安", Toast.LENGTH_SHORT).show();
                super.onLongPress(e);
            }
            /**
             * @param e1//e1按下
             * @param e2//e2離開
             * @param distanceX 在x軸滑動了距離
             * @param distanceY 在Y軸滑動了距離
             * @return
             */
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                //區別
                //scrollTo相對於view的初始位置移動,所以這裡view無論點選多少次,都只會相對於view的初始位置移動一定距離。
                //mLayout.scrollTo(getResources().getDimensionPixelSize(R.dimen.a), getResources().getDimensionPixelSize(R.dimen.b));
                //scrollBy相對於view的當前位置移動,所以此處view是每點選一次就向右下角移動一次的。
                //mLayout.scrollBy(getResources().getDimensionPixelSize(R.dimen.a), getResources().getDimensionPixelSize(R.dimen.b));


                //讓自己內容移動(不是讓孩子移動)
                //根據當前位置移動:getScrollY初始值
                //x:要在x軸平移的距離
                //y:要在y軸移動的距離
                //看點
                //理解相對論(右移動是負)|(左移動是正)
                Log.e("hh", "onScroll: "+distanceX );
                scrollBy((int)distanceX,getScrollY());
                return super.onScroll(e1, e2, distanceX, distanceY);
            }

            @Override
            public boolean onDoubleTap(MotionEvent e) {
                Toast.makeText(context, "雙擊", Toast.LENGTH_SHORT).show();
                return super.onDoubleTap(e);
            }
        });
    }


    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
    //便利孩子,給每個孩子指定在螢幕的座標位置
        for (int j = 0; j < getChildCount(); j++) {
            View childAt = getChildAt(j);
            //l距離左邊距離t距離top上邊距離,(一個上點)|r距離左(一個下點)b距離上面
            childAt.layout(j*getWidth(),0,(j+1)*getWidth(),getHeight());
        }
    }
    //判斷觸控事件的傳遞(我只要x軸用來滑倒下一頁)

    //如果當前,方法返回true,攔截事件,將會觸發當前控制元件的onTouchEvent()方法
    //如果當前,方法返回false,不攔截事件,事件繼續傳遞給孩子
    //攔截事件
    private float mStarX;
    private float mStarY;


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //因為我在這做了一點滑動判斷所以哪一點也需要傳入手指介面卡不來會跳一下
        mGestureDetector.onTouchEvent(ev);
        boolean issorllX=false;
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN://按下
                //1,記錄按下的xy值
                mStarX = ev.getX();
                mStarY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE://移動
                //2,記錄結束xy值
               float endX = ev.getX();
                float endY = ev.getY();
                //3,計算絕對值
                float distanceX  =Math.abs(mStarX-endX);
                float  distanceY  =Math.abs(mStarY-endY);
                if(distanceX>distanceY&&distanceX>10){
                    issorllX= true;
                }else{
                    scrollToPager(currentIndx);
                }
                break;
            case MotionEvent.ACTION_UP://抬起
                break;
        }

        return issorllX;
    }

    private float startX;
//    getX()是表示Widget相對於自身左上角的x座標
//    而getRawX()是表示相對於螢幕左上角的x座標值(注意:這個螢幕左上角是手機螢幕左上角,不管activity是否有titleBar或是否全螢幕),getY(),getRawY()一樣的道理
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        //3,在ontouchEvent()把事件鑽地給手勢識別器
        mGestureDetector.onTouchEvent(event);
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN://按下
                //1記錄座標
                startX=event.getX();
                break;
            case MotionEvent.ACTION_MOVE://移動
                break;
            case MotionEvent.ACTION_UP://抬起
                //2來到新座標
                float endx=event.getX();
                //下表位置

                int tempIndex=currentIndx;
                if((startX-endx)>getWidth()/2){
                    //顯示下一個頁面
                    tempIndex++;
                }else if ((endx-startX)>getWidth()/2){
                    //顯示上一個頁面
                    tempIndex--;
                }
                scrollToPager(tempIndex);
                break;
        }
            return true;
    }
//遮蔽非法值 根據位置移動到指定頁面
    public void scrollToPager(int tempIndex) {
        if(tempIndex<0){
            tempIndex=0;
        }
        if(tempIndex>getChildCount()-1){
            tempIndex=getChildCount()-1;
        }
        //當前頁面的下標位置
        currentIndx=tempIndex;
        //distanceX就是滑動時的剩餘距離
        int distanceX=currentIndx*getWidth()-getScrollX();
        if(mOnpagerChanglistenter!=null){
            mOnpagerChanglistenter.onScrollToPager(currentIndx);
        }
        //移動到指定的位置
//        scrollTo(currentIndx*getWidth(),getScrollY());
//        mScrolier.startScroll(getScrollX(),getScrollY(),distanceX,0);
        mScrolier.startScroll(getScrollX(),getScrollY(),distanceX,0,Math.abs(distanceX));
        invalidate();//onDraw();computeScroll()
    }
    private OnpagerChanglistenter mOnpagerChanglistenter;
    //讓使用者傳遞介面的例項過來
    public void setOnpagerChanglistenter(OnpagerChanglistenter l){
        mOnpagerChanglistenter=l;
    }
    //監聽頁面的改變
    //定義介面
    public interface OnpagerChanglistenter{
        //當頁面改變的時候回掉這個方法position當前頁面的下表
        void onScrollToPager(int position);
    }

    @Override
    public void computeScroll() {
        if(mScrolier.computeScrollOffset()){
            float currX = mScrolier.getCurrX();
            Log.e("hh", "computeScroll: "+currX );
            scrollTo((int)currX,0);
            invalidate();//onDraw();computeScroll()
        }
    }
    //onLayout設定每個孩子在當前的位置大小
    //但如果孩子是佈局的話,沒有給的控制元件測量大小,因控制元件沒有測量大小所以就不會顯示出來
    //遍歷孩子,給每個孩子指定在螢幕的座標位置
    //獲取每一個View孩子進行測量(如佈局:目的就是讓他的孩子也執行onMeasure測量)
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        for (int i = 0; i < getChildCount(); i++) {
            View chid=getChildAt(i);
            //如果孩子是ViewGroup那麼就會進去測量ViewGroup的孩子View
            chid.measure(widthMeasureSpec,heightMeasureSpec);
        }
    }
    //測量onMeasure()說明: lt點(左上)|rb點(右下)  寬度: 右邊的距離減掉左邊的距離
    //                                         高度: 底部的距離減掉頂部的距離

    /**
     * 1. 測量的時候測量多次
     * 2. widthMeasureSpec父層檢視給當前檢視的寬和模式
     * 系統的onMeasure所幹的事:
     * 1, 根據widthMeasureSpec求得寬度,和父類view給的模式(高以此類推)
     * 2, 根據自身的寬度width,和自身的padding  值,相減(減去兩邊){父類的寬頻減去padding值},求得子View可以擁有的寬度newWidth
     * 3, 根據newWidth和模式求得一個新的MeasureSpec值:
     * MeasureSpec.makeMeasureSpec(newSize,newmode);
     * 用新的MeasureSpec來計算View
     *
     */

}

上面打完就完成了效果(下面是對慢慢滑動計算的類)

package com.example.administrator.viewgroup_xianxi;

import android.os.SystemClock;

public class MyScroller {
    //起始座標
    private long totTime=500;
    private float scrollX;
    private float scrollY;
    //移動的距離
    private int distanceX;
    private int distanceY;
    //kaijishijian
    private long startTime;
    //是否移動完成
    private boolean isFinish;
    private float mCurrX;
    //得到座標
    public float getCurrX() {
        return mCurrX;
    }
    public void startScroll(float scrollX, float scrollY, int distanceX, int distanceY) {
        this.scrollX=scrollX;
        this.scrollY=scrollY;
        this.distanceX=distanceX;
        this.distanceY=distanceY;
        this.startTime= SystemClock.uptimeMillis();//體統開機時間
        this.isFinish=false;
    }
    //true正在移動
    //fles移動結束
    public boolean computeScrollOffset(){
        if(isFinish){
            return false;
        }
        long endTime=SystemClock.uptimeMillis();
        //這一小段所發揮的時間
        long passTime=endTime-startTime;
        if(passTime<totTime){
            //還沒有移動結束
            //計算平均速度
//            float voleCity=distanceX/totTime;
            //移動這一小段對應的距離
            float distanceSamllx=passTime*distanceX/totTime;
            mCurrX = scrollX + distanceSamllx;

        }else{
            //移動結束
            isFinish=true;
            mCurrX = scrollX + distanceX;
        }



        return true;
    }
}

位置理解圖


11863487-9bf2aff51fe2f13f.PNG
IMG_0660.PNG

相關文章