精华内容
下载资源
问答
  • 实现如上展示的两种效果,我们需要两个东西来帮助实现——clipChildren和PagerTransformer 1)clipChildren 这个属性见得不多,可能很多小伙伴不熟悉,这个属性是个布尔值,clip中文为裁剪的意思,clipChildren即...

    效果图

    基本原理

    要实现如上展示的两种效果,我们需要两个东西来帮助实现——clipChildrenPagerTransformer

    1)clipChildren

    这个属性见得不多,可能很多小伙伴不熟悉,这个属性是个布尔值,clip中文为裁剪的意思,clipChildren即为裁剪孩子(硬核翻译)。一般修饰在ViewGroup上,它可以表示是否限制处于容器内部的子控件可以越界绘制,默认为true。这么说还是有点懵逼,直接看图吧。

    相信大家经常见到闲鱼这种样式的底部Tab栏,那么这是如何实现的呢,肯定有人想过用布局嵌套并设置底层布局背景透明的这种方法,这种方法的确可行,可是稍嫌麻烦,如果是使用clipChildren,则要简单许多。

    比如,我现在实现一个类似效果的Tab栏,不使用clipChildren是这样的。

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        ...>
    
        <LinearLayout
            android:layout_height="60dp"
            ...>
    
            <ImageView
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:src="@mipmap/ic_launcher" />
    
            <ImageView
                ... />
    
            <ImageView
                android:layout_width="0dp"
                android:layout_height="80dp"
                android:layout_gravity="bottom"
                android:layout_weight="1"
                android:src="@mipmap/ic_launcher" />
    
            <ImageView
                ... />
    
            <ImageView
                .../>
        </LinearLayout>
    </RelativeLayout>

    可以看见,中间的tab明显被裁减了,那么加上clipChildren属性呢?

    android:clipChildren="false"

    当我们为根节点设置clipChildren为false时,神奇的一幕出现了,中间的tab竟然长出头了(滑稽)。通过这个例子,你应该了解了clipChildren的作用了。

    2)PagerTransformer

    PagerTransformer是ViewPager暴露给我们的一个接口,它内部含有一个transformPage(view:View,position:Float)方法,通过这个方法我们可以拿它的两个参数针对view实现各种切换效果。这里着重讲一下方法的第二个参数,它表示每个view的下标,但是注意它是一个Float类型的变量,它不像我们以往见的下标那样,1就是1,2就是2,它是会变的,动态的。刚才为0的下标,滑动过后可能会变成1或-1,也就是在[1-,0],[0,1]之间变化,0并不是他的最小值并且当前展示的view下标永远为0。

    话不多说,上图

    就记住一句话,对于下标,当前展示永为0,左为负递减,右为正递增(总结的我都想给自己个赞。。)。

    对于PagerTransformer,官方有俩个实现,有兴趣的可以去看看。传送门

    动手实现

    为了实现文章开篇两幅图的效果,首先搞定vp单屏显示多页效果。

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:gravity="center_horizontal"
        tools:context=".MainActivity">
    
        <android.support.v4.view.ViewPager
            android:id="@+id/view_pager"
            android:overScrollMode="never"
            android:layout_width="240dp"
            android:clipChildren="false"
            android:layout_height="wrap_content" />
    
    </LinearLayout>

    因为vp默认只显示一页内容,所以需要两个根节点都要加上clipChildren属性。如果需要每页有间隔的可以通过代码设置

    view_pager.pageMargin = 15

    画廊效果

    private const val MIN_SCALE_Y = 0.75f
    private const val MIN_SCALE_X = 0.95f
    private const val MIN_ALPHA = 0.7f
    
    class MyTransform : ViewPager.PageTransformer {
    
        override fun transformPage(view: View, position: Float) {
            view.apply {
                when {
                    position < -1 -> { // [-∞,-1)
                        // 屏幕左边的view
                        alpha = MIN_ALPHA
                        scaleX = MIN_SCALE_X
                        scaleY = MIN_SCALE_Y
                    }
                    position <= 1 -> { // [-1,1]
                        //X/Y方向上的缩放比例,跟随下标变化,介于分别的最小值和1之间
                        val scaleFactorY = (MIN_SCALE_Y + (1 - MIN_SCALE_Y) * (1 - Math.abs(position)))
                        val scaleFactorX = (MIN_SCALE_X + (1 - MIN_SCALE_X) * (1 - Math.abs(position)))
                        scaleX = scaleFactorX
                        scaleY = scaleFactorY
                        // 随着下标变化的透明度
                        alpha = (MIN_ALPHA + ((1 - Math.abs(position)) * (1 - MIN_ALPHA)))
                    }
                    else -> { // (1,+∞]
                        // 屏幕右边的view
                        alpha = MIN_ALPHA
                        scaleX = MIN_SCALE_X
                        scaleY = MIN_SCALE_Y
                    }
                }
            }
        }
    }

    屏幕两侧看不到的view的透明度和缩放大小永远是固定的,避免它们从两侧出现时大小和透明度的突然变化带来的落差感。屏幕显示的view的透明度和大小随着手指切换时在一个区间动态改变。

    画廊式效果图

    侧旋式效果

    private const val MIN_SCALE = 0.75f
    
    class DashTransform : ViewPager.PageTransformer {
        override fun transformPage(view: View, position: Float) {
            view.apply {
                val pageWidth = width
                when {
                    position < -1 -> { // [-∞,-1)
                        alpha = 0f
                    }
                    position <= 0 -> { // [-1,0] 移动到左侧的view
                        alpha = 1f - Math.abs(position)
                        scaleX = 1f
                        scaleY = 1f
                        rotation = 30 * position
                    }
                    position <= 1 -> { // (0,1] 从右侧移动过来的view
                        alpha = 1 - position
                        // 抵消掉原来的位移,让view固定住
                        translationX = pageWidth * -position
                        // 缩放渐变大小
                        val scaleFactor = (MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)))
                        scaleX = scaleFactor
                        scaleY = scaleFactor
                    }
                    else -> { // (1,+∞]
                        alpha = 0f
                    }
                }
            }
        }
    }

    向左移动的view透明度随着下标逐渐透明,并且加了一个30度以内的旋转,原本从右侧移动过来的view被固定在了向左移动的view的下面,因为代码设置的位移抵消掉了它原本的移动距离,使它看起来像是一只固定在一个位置。

    侧旋式效果

    最后

    其实很多炫酷的效果,乍一看以为很难,其实无非就是通过旋转、位移、缩放、渐变组合起来实现的,只要找到合适的地方和方法,合理利用组合就能实现非常棒的效果,看了这篇文章,对vp的切换效果感兴趣的小伙伴可以动手实现一个自己的效果,学以致用,加深对动画的理解。

    扩展

    基于本次博客的内容,我们还可以再次添加修改,定制实现一个带切换效果的banner,集体内容就看下篇博客吧~

    展开全文
  • 其实我这个就是实现四个点的效果,通过滑动viewpager来选中当前的点进行变色类似这种效果图,颜色可以自己修改还有大小不多说直接上代码自定义属性xml&lt;?xml version="1.0" encoding="utf-8&...

    其实我这个就是实现四个点的效果,通过滑动viewpager来选中当前的点进行变色

    类似这种效果图,颜色可以自己修改还有大小

    不多说直接上代码

    自定义属性xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="PagePointView">
            <attr name="circleInterval" format="dimension"/>
            <attr name="circleSizi" format="dimension"/>
            <attr name="circleNumber" format="integer"/>
            <attr name="selectIndex" format="integer"/>
            <attr name="circleSelectColor" format="color"/>
            <attr name="circleUnSelectColor" format="color"/>
        </declare-styleable>
    </resources>

    代码

    public class PagePointView extends View {
        private Paint mPaint;
        private int mCircleInterval;//设置圆和点圆之间的间隔
        private int mCircleRadius;//设置圆的大小
        private int mCircleNumber;//设置圆的个数
        private int mSelectIndex = 1;//设置选中的圆
        private int mScreenWidht;
        private int mScreenHeight;
        private int mWidht;
        private int mHeight;
        private int mSelectColor;
        private int mUnSelectColor;
    
    
        public void setmCircleNumber(int mCircleNumber) {
            this.mCircleNumber = mCircleNumber;
        }
    
        public void setIndex(int selectIndex) {
            if (selectIndex > mCircleNumber) {
                this.mSelectIndex = mCircleNumber;
            } else {
                this.mSelectIndex = selectIndex;
            }
            postInvalidate();
        }
    
        public void setmSelectColor(int mSelectColor) {
            this.mSelectColor = mSelectColor;
        }
    
        public void setmUnSelectColor(int mUnSelectColor) {
            this.mUnSelectColor = mUnSelectColor;
        }
    
        public PagePointView(Context context) {
            this(context, null);
        }
    
        public PagePointView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public PagePointView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setColor(Color.BLACK);
            mPaint.setStrokeWidth(20);
            mPaint.setStyle(Paint.Style.FILL);
            mScreenWidht = context.getResources().getDisplayMetrics().widthPixels;
            mScreenHeight = context.getResources().getDisplayMetrics().heightPixels;
    
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PagePointView, defStyleAttr, 0);
            int count = ta.getIndexCount();
            for (int i = 0; i < count; i++) {
                int index = ta.getIndex(i);
                switch (index) {
                    case R.styleable.PagePointView_circleInterval:
                        mCircleInterval = ta.getDimensionPixelSize(index, (int) TypedValue.applyDimension
                                (TypedValue.COMPLEX_UNIT_PX, 12, context.getResources().getDisplayMetrics()));
                        break;
                    case R.styleable.PagePointView_circleNumber:
                        mCircleNumber = ta.getInt(index, 4);
                        break;
                    case R.styleable.PagePointView_circleSizi:
                        mCircleRadius = ta.getDimensionPixelOffset(index, (int) TypedValue.applyDimension
                                (TypedValue.COMPLEX_UNIT_DIP, 12, context.getResources().getDisplayMetrics()));
                        break;
                    case R.styleable.PagePointView_selectIndex:
                        mSelectIndex = ta.getInt(index, 1);
                        break;
                    case R.styleable.PagePointView_circleSelectColor:
                        mSelectColor = ta.getColor(index, Color.WHITE);
                        break;
                    case R.styleable.PagePointView_circleUnSelectColor:
                        mUnSelectColor = ta.getColor(index, Color.BLACK);
                        break;
                }
            }
            ta.recycle();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int wMode = MeasureSpec.getMode(widthMeasureSpec);
            int wSize = MeasureSpec.getSize(widthMeasureSpec);
            int hMode = MeasureSpec.getMode(heightMeasureSpec);
            int hSize = MeasureSpec.getSize(heightMeasureSpec);
            int viewWidht = 0;
            int viewHeight = 0;
            switch (wMode) {
                case MeasureSpec.EXACTLY:
                    viewWidht = wSize;
                    break;
                case MeasureSpec.AT_MOST:
                    viewWidht = mCircleInterval + getPaddingLeft() + getPaddingRight() + mCircleRadius * 2;
                    break;
                case MeasureSpec.UNSPECIFIED:
                    viewWidht = wSize;
                    break;
            }
            switch (hMode) {
                case MeasureSpec.EXACTLY:
                    viewHeight = wSize;
                    break;
                case MeasureSpec.AT_MOST:
                    viewHeight = getPaddingBottom() + getPaddingTop() +  mCircleRadius * 2;
                    break;
                case MeasureSpec.UNSPECIFIED:
                    viewHeight = wSize;
                    break;
            }
            setMeasuredDimension(resolveSize(viewWidht, widthMeasureSpec), resolveSize(viewHeight, heightMeasureSpec));
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mWidht = w;
            mHeight = h;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int mViewWidht;
            for (int i = 1; i <= mCircleNumber; i++) {
                if (mSelectIndex == i) {
                    mPaint.setColor(mSelectColor);
                    canvas.drawCircle((2 * i - 1) * mCircleRadius + mCircleInterval * (i - 1), mCircleRadius, mCircleRadius, mPaint);
                    continue;
                }
                mPaint.setColor(mUnSelectColor);
                canvas.drawCircle((2 * i - 1) * mCircleRadius + mCircleInterval * (i - 1), mCircleRadius, mCircleRadius, mPaint);
            }
        }
    
    
    }

    使用注意:必须在使用的布局xml文件的最外层布局中加上chipChildren=“false”否则只会显示选中的点

    使用在viewpager的监听中设置选中的点位置

    private class PageChangeListener implements ViewPager.OnPageChangeListener {
            @Override
            public void onPageScrollStateChanged(int position) {
    
            }
    
            @Override
            public void onPageScrolled(int position, float arg1, int arg2) {
    
            }
    
            @Override
            public void onPageSelected(int position) {
                // 设置底部小点选中状态
                pagePoint.setIndex(position+1);
            }
    
        }
    可以将上面代码封装到自定义PagePointView中这里没有做过多操作,赖,我的原则能用就行封装后面在做


    展开全文
  • 自定义ViewPager,设置ViewPager的背景图片,当ViewPager切换页面时,背景图片自动滚动,根据计算,当ViewPager切换到最后一个页面时,背景也同时滑动到最后,如果背景图片宽度较小,ViewPager每次切换时,背景图片...
  • 在项目开发中,有时候需要用到Viewpager,却不需要它的左右滑动,这个时候可以自定义Viewpager,通过对它的事件分发方法返回值的修改实现Viewpager左右滑动的禁止.具体代码如下: public class NoScrollViewPager ...

    需求

    在项目开发中,有时候需要用到Viewpager,却不需要它的左右滑动,这个时候可以自定义Viewpager,通过对它的事件分发方法返回值的修改实现对Viewpager左右滑动的禁止.具体代码如下:

    public class NoScrollViewPager extends ViewPager {
        private boolean isScroll = false;
    
        public NoScrollViewPager(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public NoScrollViewPager(Context context) {
            super(context);
        }
    
        /**
         * 1.dispatchTouchEvent一般情况不做处理
         * ,如果修改了默认的返回值,子孩子都无法收到事件
         */
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            return super.dispatchTouchEvent(ev);   // return true;不行
        }
    
        /**
         * 是否拦截
         * 拦截:会走到自己的onTouchEvent方法里面来
         * 不拦截:事件传递给子孩子
         */
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            // return false;//可行,不拦截事件,
            // return true;//不行,孩子无法处理事件
            //return super.onInterceptTouchEvent(ev);//不行,会有细微移动
            if (isScroll) {
                return super.onInterceptTouchEvent(ev);
            } else {
                return false;
            }
        }
    
        /**
         * 是否消费事件
         * 消费:事件就结束
         * 不消费:往父控件传
         */
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            //return false;// 可行,不消费,传给父控件
            //return true;// 可行,消费,拦截事件
            //super.onTouchEvent(ev); //不行,
            //虽然onInterceptTouchEvent中拦截了,
            //但是如果viewpage里面子控件不是viewgroup,还是会调用这个方法.
            if (isScroll) {
                return super.onTouchEvent(ev);
            } else {
                return true;// 可行,消费,拦截事件
            }
        }
    
        public void setScroll(boolean scroll) {
            isScroll = scroll;
        }
    }
    

    在代码中特意增加了 boolean类型的 noScroll,可以控制是否禁止左右滑动,

    展开全文
  • 自定义简单ViewPager

    2017-11-22 10:38:10
    需要实现ViewPager,首先要准备数据源,setAdapter就可以将图片设置好 其中最重要的就是设置小圆点的移动,其中做主要的代码如下: viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { ...

    需要实现ViewPager,首先要准备数据源,setAdapter就可以将图片设置好

    其中最重要的就是设置小圆点的移动,其中做主要的代码如下:

    viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                    float offces = mMaxOffces * (position + positionOffset);
                    //获取布局参数
                    FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) ivRedDot.getLayoutParams();
                    params.leftMargin = (int) (offces + 0.5f);//设置滑动的左边距
                    ivRedDot.getParent().requestLayout();//让父控件重新布局
                }
    
                @Override
                public void onPageSelected(int position) {
    
                }
    
                @Override
                public void onPageScrollStateChanged(int state) {
    
                }
            });
    
            llDot.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    //设置两圆点之间的间距
                    mMaxOffces = llDot.getChildAt(1).getLeft() - llDot.getChildAt(0).getLeft();
                    //用过一次后就移除
                    llDot.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }
            });

    源代码:http://download.csdn.net/download/k2514091675/10128120


    展开全文
  • Android自定义View实现ViewPager指示器
  • Android自定义弧形ViewPager

    千次阅读 2020-03-20 21:57:57
    自定义弧形View。如下所示。 圆和屏幕相交的地方就是我们所需要的弧形,当然也可以使用贝塞尔曲线绘制。 半径为测量布局宽度*2 绘制过程如下: @Override protected void onDraw(Canvas canvas) { canvas....
  • 一个自定义控件,实现ViewPager中拖动功能,并自定义相关控制事件
  • 实现自定义ViewPager public class MyViewPager extends ViewGroup { public MyViewPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onLayout(boolean changed, ...
  • Android自定义控件实现ViewPagerIndicator
  • 自定义ViewPager实现循环滑动翻页,广告页的形式。 自定义ViewPager实现循环滑动翻页,广告页的形式。
  • 自定义ViewPager实现Banner,知识点包含按下或者滑动的过程中停止轮播
  • 自定义viewpager 实现竖直viewpager 的效果,附带限制viewpager 是否可以滑动 : import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import ...
  • 为了实现 ViewPager 切换 Fragment 时的标签效果(类似新闻客户端导航的效果) 代码 package com.demo.view; import android.content.Context; import android.content.res.TypedArray; import android....
  • 效果图步骤 继承HorizontalScrollView ...绑定ViewPager 根据ViewPager.setOnPageChangeListener来处理我们画Tab下划线的逻辑 在dispatchDraw中画线 实现 继承HorizontalScrollView public class ViewPagerIndic
  • 继承自ViewGroup然后配合Scroller完成滑动效果 主要弄清楚scrollTo和scrollBy 及scroller的用法即可很容易实现
  • 自定义 viewpager

    2017-11-22 10:34:25
    自定义viewpager实现左右滑动的引导页,自定义viewpager实现左右滑动的引导页
  • 因近些端开发的app项目的老师使用到在Fragment中放ViewPagerViewPager中再套用Fragment,指示器经常用第三方的也没有新意,看到网上的大神好多种方法实现,就琢磨者能不能自己写一个,说干就干,因博客写的少,...
  • 先看一下效果图(左右无限滑动,自动轮播)地铁上看了一个大神的博客,自定义了通用viewpager,但是没有自动轮播和左右无限滑动功能,自己尝试写了一下。在这给大家分享一下,也给大神传播一下自定义控件的思想。...
  • 用到的知识: 1.ViewPager和Fragment的结合; 2.自定义view; 3.绘图。
  • ViewPager用于实现多页面的切换效果,该类存在于Google的兼容包里面,所以在引用时记得在BuilldPath中加入“android-support-v4.jar”主布局文件main.xml查看源码 打印?01<?xmlversion="1.0"encoding="utf-8"?&...
  • 我们来实现一个轮播图片的自定义ViewPager。 1、继承ViewGroup,并写个添加图片数据的方法,方便添加图片到ViewGroup容器里 package com . wong . support ; import android . content . Context ; ...
  • 本文没有提供酷炫的动画实现,...从3.0开始,ViewPager开始支持自定义切换动画,官方提供的接口为PageTransformer,因此只要实现该接口即可,PageTransformer非常简单,它只有一个方法: /** * Apply a property tran

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,631
精华内容 652
关键字:

自定义实现viewpager