Android 自定义View之ViewPagerIndicator

简介

本文主要记录了学习自定义指示器的过程,从简单的组合控件到完全自定义View的实现。从中熟悉了view方面的知识。

现在各式各样的指示器很多,框架很多,炫酷的效果也很多。但是小白我在阅读大牛写的代码时,也很难读懂关键的操作,也尝试去看谷歌工程师写的控件,摄取到的知识还是有限,这也从侧面说明了,本人的自定义View技能很薄弱,加之一些常用的API掌握的不是很好,也为自己敲响了警钟了,所以我希望从点滴从前辈中学习简易的自定义view知识,而最最常见的是各种进度条以及知识器,本文是对指示器的积累,也是从很基础的开始,我现在追求的是实现一定的效果无法满足拓展性,一步一个台阶吧。也是慢慢的在这个过程中不断的完善,也主要是借鉴别人写的控件。

运行效果

从上至下为简易,自定义一般到比较不错的自定义效果。

                                        

 

 

 

1.通过RadioGroup+ImageView实现指示器滑动效果

 

如上效果,和TabLayout相类似,但是必然是比TabLayout差很多的。主要的思路是使用RadioGroup作文字的单选,通过ImageView的位置来指示选定了哪一个tab,这里主要通过ViewPager滑动来设置指示器的位置。在看代码前有些小的知识点需要关注。

1. setLayoutParams()

通过为某一个特定的View设置参数可以改变该控件的位置

    public void setLayoutParams(ViewGroup.LayoutParams params) {
        if (params == null) {
            throw new NullPointerException("Layout parameters cannot be null");
        }
        mLayoutParams = params;
        resolveLayoutParams();
        if (mParent instanceof ViewGroup) {
            ((ViewGroup) mParent).onSetLayoutParams(this, params);
        }
        requestLayout();
    }

设置指示器的位置,我们需要获得指示器的params配置属性,该属性包决定了在父组件的位置信息,为指示器包一层线性布局,那么就可以设置左右的margin来达到滑动的效果。注意得强转为LinearLayout.LayoutParams,方能够设置leftMargin 

        //*****
        // 只有其父布局为LinearLatout时,才能够有 LinearLayoutParams,才能够有leftMargin 设置左边间距
        //*****
        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mIvLinear.getLayoutParams();
        layoutParams.width = lineWidth;
        layoutParams.leftMargin = getScreenWidthWidth()/6 - lineWidth/2;
        mIvLinear.setLayoutParams(layoutParams);

2.ViewPager

 通过addOnPageChangedListener 可以监听页面的滑动,那有必要对下面的参数进行探讨

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                Log.e("onPageScrolled",position+",   "+positionOffset+",   "+positionOffsetPixels);
            }

界面1-界面2 log日志

从下面日志 我们可以发现第一个参数值直接从0->1,第二个参数值从0.0依次增加到0.9xx无限靠近1,然后页面到达第二页它又恢复成了0,第三个参数从1开始累积到719+(这个没有参考加之)

E/onPageScrolled: 0,   0.0,   0
E/onPageScrolled: 0,   0.0,   0
E/onPageScrolled: 0,   0.019444445,   14
E/onPageScrolled: 0,   0.045833334,   33
E/onPageScrolled: 0,   0.079166666,   57
E/onPageScrolled: 0,   0.10138889,   73
E/onPageScrolled: 0,   0.119444445,   86
E/onPageScrolled: 0,   0.13333334,   96
E/onPageScrolled: 0,   0.14861111,   107
E/onPageScrolled: 0,   0.15972222,   115
E/onPageScrolled: 0,   0.19305556,   139
.......
E/onPageScrolled: 0,   0.98055553,   706
E/onPageScrolled: 0,   0.9847222,   709
E/onPageScrolled: 0,   0.98888886,   712
E/onPageScrolled: 0,   0.9916667,   714
E/onPageScrolled: 0,   0.9930556,   715
E/onPageScrolled: 0,   0.99583334,   717
E/onPageScrolled: 0,   0.99722224,   718
E/onPageScrolled: 0,   0.9986111,   719
E/onPageScrolled: 1,   0.0,   0

界面2-界面1log日志

我们可以发现第一个参数从第二页的1瞬间变为了0,之后参数一一致停留在0,而参数二大致是从1渐变到0的

E/onPageScrolled: 1,   0.0,   0
E/onPageScrolled: 0,   0.99583334,   717
E/onPageScrolled: 0,   0.9736111,   701
E/onPageScrolled: 0,   0.9527778,   686
E/onPageScrolled: 0,   0.9291667,   669
E/onPageScrolled: 0,   0.90416664,   651
E/onPageScrolled: 0,   0.87777776,   632
E/onPageScrolled: 0,   0.84444445,   608
E/onPageScrolled: 0,   0.79305553,   571
E/onPageScrolled: 0,   0.75416666,   543
E/onPageScrolled: 0,   0.70555556,   508
........
E/onPageScrolled: 0,   0.015277778,   11
E/onPageScrolled: 0,   0.011111111,   8
E/onPageScrolled: 0,   0.0069444445,   5
E/onPageScrolled: 0,   0.0055555557,   4
E/onPageScrolled: 0,   0.004166667,   3
E/onPageScrolled: 0,   0.0027777778,   2
E/onPageScrolled: 0,   0.0013888889,   1
E/onPageScrolled: 0,   0.0,   0

结论:

当ViewPager页面值为0(第一页)且当参数一为0时,页面的状态时从  第一页到第二页

当ViewPager页面值为1(第二页)且当参数一为0时,页面的状态时从  第二页到第一页

position记录的是滑动区间较小的索引,positionOffset记录的是滑动的百分小数;向右滑,positionOffset从0-1,向左滑,positionOffset 从1到0.

下面直接贴代码了

/**
 * @author crazyZhangxl on 2018-11-2 9:49:55.
 * Describe: 使用布局文件 RadioGroup+底部指示器 实现指示器
 */

public class IndicatorDemo1Activity extends AppCompatActivity {
    private ViewPager mViewPager;

    private RadioGroup mRadioMain;
    private RadioButton mRadioAc1,mRadioAc2,mRadioAc3;
    private ImageView mIvLinear;


    private int lineWidth = 80;
    private int currentPage = -1;
    private int screenWidth;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_indicator_demo1);
        initViews();
        initTabLines();
        initListener();
    }

    private void initListener() {


        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                // 对于三个标签 那么一共只有四种情况
                //{1. 0->1 1->2 1->0 2->1}
                Log.e("onPageScrolled",position+",   "+positionOffset+",   "+positionOffsetPixels);
                LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mIvLinear.getLayoutParams();
                // (position+positionOffset)*getScreenWidthWidth()/3f 为偏移量;  右边相加的是居中偏移
                layoutParams.leftMargin = (int)((position+positionOffset)*getScreenWidthWidth()/3f+getScreenWidthWidth()/6-lineWidth/2);
                mIvLinear.setLayoutParams(layoutParams);
            }

            @Override
            public void onPageSelected(int position) {
                currentPage = position;
                switch (position){
                    case 0:
                        mRadioAc1.setChecked(true);
                        break;
                    case 1:
                        mRadioAc2.setChecked(true);
                        break;
                    case 2:
                        mRadioAc3.setChecked(true);
                        break;
                    default:
                        break;
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

    }

    private void initTabLines() {
        mRadioMain = findViewById(R.id.rgMain);
        mRadioAc1 = findViewById(R.id.rbAc1);
        mRadioAc2 = findViewById(R.id.rbAc2);
        mRadioAc3 = findViewById(R.id.rbAc3);
        mRadioAc1.setChecked(true);
        mRadioMain.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                switch (checkedId) {
                    case R.id.rbAc1:
                        mRadioAc1.setChecked(true);
                        mViewPager.setCurrentItem(0);
                        break;
                    case R.id.rbAc2:
                        mRadioAc2.setChecked(true);
                        mViewPager.setCurrentItem(1);
                        break;
                    case R.id.rbAc3:
                        mRadioAc3.setChecked(true);
                        mViewPager.setCurrentItem(2);
                        break;
                    default:
                        break;
                }
            }
        });

        mIvLinear = findViewById(R.id.imageIndicator);
        //*****
        // 只有其父布局为LinearLatout时,才能够有 LinearLayoutParams,才能够有leftMargin 设置左边间距
        //****
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
旅游社交小程序功能有管理员和用户。管理员有个人中心,用户管理,每日签到管理,景推荐管理,景分类管理,防疫查询管理,美食推荐管理,酒店推荐管理,周边推荐管理,分享圈管理,我的收藏管理,系统管理。用户可以在微信小程序上注册登录,进行每日签到,防疫查询,可以在分享圈里面进行分享自己想要分享的内容,查看和收藏景以及美食的推荐等操作。因而具有一定的实用性。 本站后台采用Java的SSM框架进行后台管理开发,可以在浏览器上登录进行后台数据方面的管理,MySQL作为本地数据库,微信小程序用到了微信开发者工具,充分保证系统的稳定性。系统具有界面清晰、操作简单,功能齐全的特,使得旅游社交小程序管理工作系统化、规范化。 管理员可以管理用户信息,可以对用户信息添加修改删除。管理员可以对景推荐信息进行添加修改删除操作。管理员可以对分享圈信息进行添加,修改,删除操作。管理员可以对美食推荐信息进行添加,修改,删除操作。管理员可以对酒店推荐信息进行添加,修改,删除操作。管理员可以对周边推荐信息进行添加,修改,删除操作。 小程序用户是需要注册才可以进行登录的,登录后在首页可以查看相关信息,并且下面导航可以击到其他功能模块。在小程序里击我的,会出现关于我的界面,在这里可以修改个人信息,以及可以击其他功能模块。用户想要把一些信息分享到分享圈的时候,可以击新增,然后输入自己想要分享的信息就可以进行分享圈的操作。用户可以在景推荐里面进行收藏和评论等操作。用户可以在美食推荐模块搜索和查看美食推荐的相关信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值