自定义控件ViewPager

效果图:


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;
    }
}

位置理解图


IMG_0660.PNG
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容