美文网首页
仿写朴朴app(二)

仿写朴朴app(二)

作者: 怀念小兔 | 来源:发表于2019-03-25 16:51 被阅读0次

今天来写这个小控件:


不起眼的小控件

可能有同学要说了,这不就是ViewPagerIndicator么,去github上找xxxx。
嗯,这确实也是个解决方案,但是开头我说了,尽量不用第三方UI库,而且就朴朴里面的这个Indicator,功能很简单,也基本不存在什么太大的扩展性需求,自己写能节约一些性能。很多时候,我们只需要第三方库的一个功能,但是引用的库却包含很多我们用不到的功能,这就凭空增加了安装包的体积。
哪怕在kotlin工程里,自定义控件我一般也用java写,以便复用到java工程,这个也不例外。
操作朴朴app我们发现,这个indicator没有复杂的效果,只是在放图片的viewpager翻页的时候指示器改变状态,所以没必要继承ViewGroup,我们直接继承view,自己去画里面的内容。
首先在styleable里定义几个属性:

    <declare-styleable name="SimpleIndicator">
        <!-- 指示器每个子项的宽度 -->
        <attr name="indicatorItemWidth" format="dimension|reference"/>
        <!-- 指示器每个子项间的间隔 -->
        <attr name="indicatorItemPadding" format="dimension|reference"/>
        <!-- 指示器每个子项的圆角半径 -->
        <attr name="indicatorItemRadius" format="dimension|reference"/>
        <!-- 指示器选中项的颜色 -->
        <attr name="indicatorItemSelectedColor" format="color|reference"/>
        <!-- 指示器未选中项的颜色 -->
        <attr name="indicatorItemNormalColor" format="color|reference"/>
    </declare-styleable>

在这里,子项的高度直接取外层高度就行了,因为这个控件并不需要padding。

为了让这个控件宽度能够使用wrap_content指定,必须重写它的onMeasure方法,根据我们自定义的子项宽度和间距来计算总宽度,MeasureSpec的用法网上一搜一大片,不再赘述:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        //宽度使用wrap_content的时候
        if(widthMode == MeasureSpec.AT_MOST){
            float width = getPaddingLeft() + getPaddingRight() + itemWidth * size + (size - 1) * itemPadding;
            if(width < widthSize)
                widthSize = (int)width;
            else
                throw new IllegalStateException("您设置的子项目宽度和间距超过了可用宽度");
        }
        //高度必须指定为具体值
        if(heightMode != MeasureSpec.EXACTLY){
            throw new IllegalStateException("indicator的高度必须指定为具体数值");
        }
        setMeasuredDimension(widthSize, heightSize);
    }

然后是子项绘制位置的初始化:

    private void initItemRectangles(){
        itemRectangles = new RectF[size];
        int height = getMeasuredHeight();
        float itemDist = itemWidth + itemPadding;
        for(int i = 0; i < size; i++){
            float left = i * itemDist;
            float right = left + itemWidth;
            itemRectangles[i] = new RectF(left, 0, right, height);
        }
    }

这个初始化应该在onDraw里调用并且只调用一次以避免多余的计算,大家可能注意到了drawXXXX这类方法也有不用RectF的重载,直接以上下左右坐标为入参,但是如果你那样用,每次onDraw的时候都会重新计算一次,作为硬件的老板,我们要学会为这些可爱的员工减负啊。
上面的size哪来的?当然是跟viewPager关联的时候取到的了:

    public void setupWithViewPager(ViewPager viewPager){
        PagerAdapter adapter = viewPager.getAdapter();
        if(adapter == null)
            throw new IllegalStateException("this viewPager has not set adapter yet");
        this.size = adapter.getCount();
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int i, float v, int i1) {

            }

            @Override
            public void onPageSelected(int i) {
                selectedIndex = i;
                invalidate();
            }

            @Override
            public void onPageScrollStateChanged(int i) {

            }
        });
    }

最后是onDraw方法:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(itemRectangles == null)
            initItemRectangles();
        for(int i = 0; i < itemRectangles.length; i++){
            drawItem(canvas, itemRectangles[i], i == selectedIndex);
        }
    }
    protected void drawItem(Canvas canvas, RectF rect, boolean selected){
        paint.setColor(selected ? selectedColor : normalColor);
        canvas.drawRoundRect(rect, itemRadius, itemRadius, paint);
    }

这里把drawItem抽成单独的方法,而且是protected,一般这种Indicator的不同只是图形不同,这样如果你需要简单扩展的话,改这个方法就可以了。
到这里,自定义小控件就写完了,上一张效果图:

最终效果
本项目地址:仿写朴朴app

相关文章

  • 仿写朴朴app(二)

    今天来写这个小控件: 可能有同学要说了,这不就是ViewPagerIndicator么,去github上找xxxx...

  • 仿写朴朴app(一)

    最近发现朴朴app的样式比较漂亮,决定闲暇时间试着仿写一下UI亮点部分,顺便复习一下安卓原生知识和kotlin。由...

  • 朴朴和魔法森林 2

    第二章:阿刺的家(上) 朴朴、小豆芽和阿刺来到了魔法森林的入口...... “站住!不得入内!”警卫老虎向朴朴吼道...

  • 情粽系乡情

    端午临近,又逢双休,打开手机,淘宝,叮咚买菜,朴朴买菜等等APP,各类关于粽子的广告铺天盖地,有花生板栗馅儿,腊肉...

  • 妈妈与朴朴

    一个偏僻的仓库,每天数辆绿色的电动车载着货物进进出出。电动车后面的箱子上有两个字,凑近一看:朴朴。 妈...

  • 【朴树】大麻科,朴属

    原归属为榆科,朴属。 学名:Celtis sinensis Pers.; 别名:黄果朴、紫荆朴、小叶朴。 落叶乔木...

  • 腦洞

    朴燦熙(李棟旭飾)和朴善熙(柔光李棟版)姊弟二人被朴代表(鄭雨盛飾)收養,姊姊成為朴代表的商業間諜,利用她豔如桃李...

  • 朴朴四轮面经-朴朴技术研发中心

    4月27日完成了朴朴的四轮面试(三轮技术面+一轮HR面),最终成功拿到了offer。下面分享一下面试中我被问到的问...

  • 大街上看到人都在说我最近好潮好时尚呢,我的人都有一个人的时候我就不想再去看了一个人的时候我就不想再去看了一个人的时...

  • 朴本名姓卜,但因为长了一副纵欲过度的样子,所以大家都叫他朴。 其实卜这个姓氏大有来头,源于孔子七十二弟子卜商,而后...

网友评论

      本文标题:仿写朴朴app(二)

      本文链接:https://www.haomeiwen.com/subject/hehbvqtx.html