D8-Android自定义控件之DotNum及item复用问题

零、前言

今天写了一个圆点数字控件,效果如下: 最主要是想借此讲一下关于ListView,或RecyclerView的复用问题。 本案例在图片选择中测试,有时间会把我的图形选择小项目写一下,现在先看这个小控件吧。 本控件绘图部分使用我的LogicCanvas绘图库:基础使用在此, 喜欢的话可以到github上看看,顺便给个star 支持属性依次:大圆颜色,圆的高度(原生宽高无效),文字,是否选中,小圆颜色。

 toly:z_Dot_BigColor="@color/cornsilk"
 toly:z_Dot_Height="@dimen/dp_56"
 toly:z_Dot_text="H"
 toly:z_Dot_isChecked="false"
 toly:z_Dot_SmallColor="@color/small_circle"

效果图.gif


一、绘制

1.绘制思路:大圆+小圆+文字+状态控制
成员变量
    /**
     * 大圆高
     */
    private float mBigHeight = dp2px(20);
    /**
     * 大圆颜色
     */
    private int mBigCircleColor = 0x88000000;
    /**
     * 小圆颜色
     */
    private int mCenterColor = 0x885DFBF9;
    /**
     * 文字
     */
    private String mText = "";
    /**
     * 是否选中
     */
    private boolean isChecked;
自定义属性
    <!--圆点数字自定义控件-->
    <declare-styleable name="DotNumView">
        <!--自定义属性名 和 类型-->
        <attr name="z_Dot_Height" format="dimension"/>
        <attr name="z_Dot_text" format="string"/>
        <attr name="z_Dot_BigColor" format="reference|color"/>
        <attr name="z_Dot_SmallColor" format="reference|color"/>
        <attr name="z_Dot_isChecked" format="boolean"/>
    </declare-styleable>
mBigHeight = ta.getDimension(R.styleable.DotNumView_z_Dot_Height, mBigHeight);
mBigCircleColor = ta.getColor(R.styleable.DotNumView_z_Dot_BigColor, mBigCircleColor);
mCenterColor = ta.getColor(R.styleable.DotNumView_z_Dot_SmallColor, mCenterColor);
mText = ta.getString(R.styleable.DotNumView_z_Dot_text);
isChecked = ta.getBoolean(R.styleable.DotNumView_z_Dot_isChecked, false);
ta.recycle();//一定记得回收!!!
绘制
 //获取绘画者
 Painter painter = PainterEnum.INSTANCE.getInstance(canvas);
 float R = mBigHeight / 2;
 if (isChecked) {//选中状态绘制
     painter.draw(sa.deepClone().r(R).ang(360).p(R, -R).fs(mCenterColor));
     painter.drawText(st.deepClone()
             .size((int) (0.6 * mBigHeight)).str(mText)
             .p(R, R + (int) (0.2 * mBigHeight)).fs(Color.WHITE));
 } else {//未中状态绘制
     painter.draw(sa.deepClone().r(R).ang(360).p(R, -R).fs(mBigCircleColor));
     painter.draw(sa.deepClone().r((float) (0.3 * R)).ang(360).p(R, -R).fs(mCenterColor));
 }
测量:取大圆高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension((int) mBigHeight, (int) mBigHeight);
    }
更新视图方法:
        //属性的get、set方法略
    /**
     * 更新视图
     * @param text 文字
     * @param checked 是否选中
     */
    public void update(String text, boolean checked) {
        mText = text;
        this.isChecked = checked;
        invalidate();
    }

二、使用:在适配器中获取item的布局时使用

//获取ImageView
final ImageView itemIv = holder.getView(R.id.id_iv_photo);
//获取DotNumView
final DotNumView dotNum = holder.getView(R.id.id_dot_check);
//设置默认图片
itemIv.setImageResource(R.drawable.no_photo);
//加点灰
itemIv.setColorFilter(Color.parseColor("#11000000"));
//加载图片
final String filePath = currentDir + File.separator + data;
//通过文件路径加载图片
ImageLoader.getInstance(3, ImageLoader.Type.LIFO).loadImage(mContext, filePath, itemIv);
//点击选中,添加遮罩
itemIv.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {//已被选择
        //如果文件夹包含了路径
        if (mSelectedImg.contains(filePath)) {
            unSelect();//移除选中
        } else {
            select();//选中
        }
        mTvSelectCount.setText("已选" + mSelectedImg.size() + "张");
    }

    /**
     * 选中时的操作
     */
    private void select() {
        //如果选中的小于9个
        if (mSelectedImg.size() < 9) {
            //将选中的filePath加入集合
            mSelectedImg.add(filePath);
            //更新dotNum的状态
            dotNum.update(mSelectedImg.size() + "", true);
            //item背景加深灰
            itemIv.setColorFilter(Color.parseColor("#77000000"));
        } else {
            //否则警告
            String alert = ResUtils.getString(mCtx, R.string.select_max_count);
            Toast.makeText(mCtx, alert, Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 移除选中的操作
     */
    private void unSelect() {
        mSelectedImg.remove(filePath);
        itemIv.setColorFilter(Color.parseColor("#22000000"));
        dotNum.update(mSelectedImg.size() + "", false);
        mTvSelectCount.setText("已选" + mSelectedImg.size() + "张");
    }
});

三、复用问题的解决方法:

百度了几个说得云里雾里,不太明白。仔细想一下,还是发挥自己的聪明才智吧 思路:用一个Map装一下选中的点和对应的数字,布局加载是动态判断一下,是否是该position的点,然后更新状态 一开始用List,然后发现需要两个字段,才改成Map 一开始声明后在获取item布局时才初始化map,怎么搞的都不对,然后想想--这是不对的

1.新建mCheckedMap
private Map mCheckedMap = new HashMap<Integer, Integer>();
2.动态恢复
//所有dotNum更新到状态
dotNum.update("", false);
//检查mCheckedMap,动态回复状态
if (mCheckedMap.containsKey(position)) {
    L.d("position:" + position + L.l());
    dotNum.update(mCheckedMap.get(position)+"", true);
    itemIv.setColorFilter(Color.parseColor("#77000000"));
}
3.选中加入Map
mCheckedMap.put(position, mSelectedImg.size());
4.不选了从Map移除
mCheckedMap.remove(position);

对于任何复用问题都可以使用这种思路来解决,好了,就到这里


后记、

1.声明:

[1]本文由张风捷特烈原创,转载请注明 [2]欢迎广大编程爱好者共同交流 [3]个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正 [4]你的喜欢与支持将是我最大的动力

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏落花落雨不落叶

【被玩坏的博客园】之canvas装饰博客园侧边栏

16520
来自专栏everhad

android自定义控件一站式入门

Android系统提供了一系列UI相关的类来帮助我们构造app的界面,以及完成交互的处理。 一般的,所有可以在窗口中被展示的UI对象类型,最终都是继承自Vie...

32500
来自专栏everhad

android自定义控件一站式入门

TODO: 待整理 自定义控件 Android系统提供了一系列UI相关的类来帮助我们构造app的界面,以及完成交互的处理。 一般的,所有可以在窗口中被展示的U...

34050
来自专栏Android中高级开发

深入分析Android动画(一)

  View动画顾名思义其作用对象为View,包含平移、缩放、旋转、透明,这四类变化分别对应着Animation的子类TranlateAnimation、Sca...

12240
来自专栏Android知识点总结

D1-从N角星开始论述自定义控件

7620
来自专栏7号代码

Android应用界面开发——自定义控件(实现俯卧撑计数器)

在介绍自定义控件之前,先学习一下关于尺寸(dp,sp,px)和Inflater的知识。

21430
来自专栏何俊林

Android View框架总结(四)View布局流程之Measure

View树的measure流程 View的measures时序图 View布局流程之measure View的measure过程 ViewGroup的mea...

23150
来自专栏我就是马云飞

我奶奶都能懂的UI绘制流程(下)!

前言 上回咱们说到ViewRootImpl.performTraversals()这个方法,从这里开始,会进入真正的View的绘制流程。第一次看的同学先去隔壁我...

194100
来自专栏一“技”之长

Java开发GUI之可编辑区域 原

    Java的awt包中提供了单行的文本编辑组件TextField与多行的文本编辑区TextArea,这两个组件都是继承自TextComponent类。

9020
来自专栏GIS讲堂

Openlayers4中图片填充的实现

31030

扫码关注云+社区

领取腾讯云代金券