Android 实战开发总结(widget篇)

在日常开发中,常常因为不同项目需求的共性,同时也是考虑到用户使用方式的共性,会有许多复用性极高的代码实现,这里特地总结一下。

有图有真相##

实例描述

如上图,这些都是日常开发中及常见APP中会遇到的样式,有些看似简单,实现起来却较为费劲;有些很常用,这里只是提供一种思路,如果你有好意见,更好的实现方式,可以在评论中反馈哦!

这里就按照图中所示,从上到下依次展开来说

文本相关##

  • 杠掉价(价格中间带一条划掉的横向

这个在购物类APP中算是常见的内容了,实现方式如下

/**
* 实现TextView 中间横线效果
*/
marketPrice.getPaint()
.setFlags(Paint.STRIKE_THRU_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
  • 价格精确到分

数字精确到分,其实从任何一种语言来说,都有很多实现方式,有很多自带的API可以参考。这里就从java语言来说两种实现方式

/**
* 实现小数点后保留两位
*/
        String valueStr = "88.8888";
        double valueDou = 88.8888;

        Price1.setText(ShoppingTools.FormatNum2(valueStr));
        Price2.setText(ShoppingTools.FormatNum2(valueDou));

这里的Price1 和 Price2 分别是上图中的88.88 和88.89,细心的你可能会说,为什么两种不一样。这里我们可以看一下ShoppingTools类中的这两个重载的静态方法:

/**
     * 截取小数点后两位,而不是四舍五入
     *
     * @param number
     * @return
     */
    public static String FormatNum2(String number) {
        String intNumber;
        if (number.contains(".")) {
            int dot_index = number.indexOf(".");

            System.err.println("the dot index is " + dot_index);

            try {
                intNumber = number.substring(0, dot_index + 3);
            } catch (Exception e) {
                intNumber = number + "0";

            }
            System.out.println(intNumber);
        } else {
            System.out.println(number);
            intNumber = number + ".00";

        }
        return intNumber;
    }

    /**
     * 小数点后保留两位
     */
    public static String FormatNum2(double f) {
        BigDecimal bd = new BigDecimal(f);
        bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
        System.err.println("------------" + bd);
        return bd.toString();

    }

这两个重载的方法,分别就不同的入参做了小数点后保留两位的操作,注意,这里不是简单的四舍五入,有时候四舍五入总有一方是要吃亏的。这里,对于double类型的数据,暂时没有想到可以直接截取两位小数,不四舍五入的方法,做过的同学可以分享一下。

  • 简单的富文本
多样化文本

对于上面这种文本变色的UI,就暂且叫做简单的富文本吧。

这种UI,最笨的办法就是几个不同颜色TextView拼接起来。可能做一两个你觉得无所谓,但是如果有需求需要你实现很多个这样的拼接组合,你是不是想和产品聊聊呢。而且作为一名程序猿,怎么可以这样呢?一定有什么高大上的方法。方法的确是有,而且还不少。这里就选两种最常用的说说。

/**
* 实现TextView文本内变色
*/
        SpannableStringBuilder builder = new SpannableStringBuilder(richStyleTv.getText().toString());
        ForegroundColorSpan redSpan = new ForegroundColorSpan(Color.RED);
        ForegroundColorSpan greenSpan = new ForegroundColorSpan(Color.GREEN);
        builder.setSpan(redSpan, 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        builder.setSpan(greenSpan, 7, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        richStyleTv.setText(builder);

好了,这么长一大段代码,就实现了上图中第一行的变色文本。你可能觉得,写这么多,就实现那么一行,还是不够高大上呀。好了,下面就是简单的写法

richStyleTv1.setText(Html.fromHtml("第<font color=green>二</font>杯半价,第<font color=green>三</font>杯免费"));

这种写法很简短,而且还能很方便的设置字体。但如你所见,也有个很明显的缺点,文本内容不可以动态修改,这一点上第一种写法代码虽多,却没有限制。关于SpannableStringBuilder,其实还有很多用法,这里的用法只是冰山一角,有兴趣的你可以去好好研究一番。

交互体验##

  • 密码可见与否

这个可以说是最常用的用户体验,登录、注册时都会用到。
实现也是很简单:

/**
         * 密码明文显示或密文显示
         */
        show.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    //明文显示
                    passwordEdit.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
                } else {
                    passwordEdit.setTransformationMethod(PasswordTransformationMethod.getInstance());
                }
//这句话确保,点击checkBox时,光标始终在最后,很有用呀  
             passwordEdit.setSelection(passwordEdit.getText().length());
            }
        });

这里关于EditText光标始终保持在最后,也是一种友好的用户体验哦。

  • 验证码倒计时

这种获取验证码后倒计时的实现,一方面给用户一种良好的体验,同时也很大程度的避免了短信炸弹的问题

/**
         * 验证码倒计时实现
         */
        timer = new TimerCount(120, 1000, getCode);
        getCode.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (TextUtils.isEmpty(phoneNum.getText().toString())) {
                    T.showShort(mContext, "不能为空");
                } else if (!ShoppingTools.isMobileNO(phoneNum.getText().toString())) {
                    T.showShort(mContext, "请输入正确的手机号");
                } else {
                    timer.start();
                }

            }
        });

这里首先判断了输入有效性,关于手机号有效性的判断在网络上有很多正则表达式实现,这里就不再赘述。

我们看一下TimerCount类

public class TimerCount extends CountDownTimer {

    private Button bnt;

    public TimerCount(long millisInFuture, long countDownInterval, Button bnt) {
        super(millisInFuture*1000, countDownInterval);
        this.bnt = bnt;
    }

    @Override
    public void onFinish() {
        // TODO Auto-generated method stub
        bnt.setClickable(true);
        bnt.setBackgroundResource(R.color.addtocar);
        bnt.setText("获取短信校验码");
    }

    @Override
    public void onTick(long arg0) {
        // TODO Auto-generated method stub
        bnt.setClickable(false);
        bnt.setBackgroundResource(R.color.yahui);
        bnt.setText("("+arg0 / 1000 + ")秒后重新获取");
    }

}

这个类继承自CountDownTimer ,并在其OnTick方法和onFinish方法中,实现构造函数中所使用View内容的动态变化。这里以Button为例,当然用ImageView也是OK 的,根据实际情况做调整即可,主要思路是一样。

  • 动态验证码实现

对于这个动态验证码,在12306购买过火车票的同学一定不陌生,从登录到购票,每一年为了回家而抢票的那几天,不知道输了多少次验证码。当然了,现在的验证码已不像图中这么简单,但在日常APP登录验证中用到的还是很多。

/**
         * 图片验证码获取及验证
         */
        codeImg.setImageBitmap(SecurityCode.getInstance().createBitmap(300, 100, 15, mContext));
        /**
         * 点击图片生成新的验证码
         */
        codeImg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                codeImg.setImageBitmap(SecurityCode.getInstance().createBitmap(300, 100, 15, mContext));
            }
        });

        verifyCode.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (SecurityCode.getInstance().Verification(inputCode.getText().toString().trim())) {
                    T.showShort(mContext, "OK");
                } else {
                    T.showShort(mContext, "WRONG");
                }
            }
        });

这里看一下SecurityCode这个类。

public class SecurityCode {

    private static final char[] CHARS = { '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
            'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
            'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };

    private static SecurityCode bmpCode;

    public static SecurityCode getInstance() {
        if (bmpCode == null)
            bmpCode = new SecurityCode();
        return bmpCode;
    }

    private static final int DEFAULT_CODE_LENGTH = 4;// 随机字符的个数

    private String code;
    private Random random = new Random();

    /**
     * 生成验证码图片
     * 
     * @param width 宽度
     * @param height 高度
     * @param size 字体大小(以sp为单位计算)
     * @param context
     * @return
     */
    public Bitmap createBitmap(int width, int height, int size, Context context) {

        int textSize = DensityUtils.sp2px(context, size);

        Bitmap codeBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(codeBitmap);

        TextPaint textPaint = new TextPaint();
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(textSize);
        textPaint.setStrokeWidth(3);
        textPaint.setStyle(Paint.Style.STROKE);
        textPaint.setTextAlign(Paint.Align.CENTER);

        code = createCode();
        int x = (width - textSize * 3) / 2;
        int y = (height + textSize) / 2;
        for (int index = 0; index < code.length(); index++) {
            textPaint.setColor(randomColor(1));
            canvas.drawText(code.charAt(index) + "", (x + textSize * index), y, textPaint);
        }
        Random random = new Random();
        for (int i = 0; i < 5; i++) {
            textPaint.setStrokeWidth(2);
            textPaint.setColor(randomColor(1));
            canvas.drawLine(random.nextInt(width), random.nextInt(height), random.nextInt(width),
                    random.nextInt(height), textPaint);
        }
        canvas.save(Canvas.ALL_SAVE_FLAG);
        canvas.restore();
        return codeBitmap;
    }

    public String getCode() {
        return code;
    }

    public boolean Verification(String input){
        if (TextUtils.isEmpty(code))
            return false;
        
        if (TextUtils.isEmpty(input))
            return false;
        
        return code.equalsIgnoreCase(input);
    }
    
    // 验证码
    private String createCode() {
        StringBuilder buffer = new StringBuilder();
        for (int i = 0; i < DEFAULT_CODE_LENGTH; i++) {
            buffer.append(CHARS[random.nextInt(CHARS.length)]);
        }
        return buffer.toString();
    }

    // 随机颜色
    private int randomColor(int rate) {
        int red = random.nextInt(256) / rate;
        int green = random.nextInt(256) / rate;
        int blue = random.nextInt(256) / rate;
        return Color.rgb(red, green, blue);
    }

}

对于这个实现的整体思路很简单,就是从定义好的数组中随机获取4个 (这里的默认值,当然也可以设定)字符,分别设置随机的4个颜色,使用Canvas绘制出一副图片。

当然了,在实际开发中,在本地动态的生成验证码并完成校验,完全就是此地无银三百两的一种做法。更好的方式是,从服务器获取图片,并且由服务器完成用户输入校验。这里只是去了解了一下这种图片验证码实现方式,以备不时之需。

  • BadgeView

这个BadgeView 也是很常见,每天打开手机,桌面上各种未读消息都会以各种小红圈加数字的方式提醒我们,赶紧去打开APP(尤其是对于某些强迫症患者)

/**
 * 角标更新
*/

        BadgeView numView = new BadgeView(mContext);
        numView.setBackground(9, getResources().getColor(R.color.enablebtn));
        numView.setTargetView(shopCarTab);
        numView.setHideOnNull(false);
        numView.setBadgeCount(0);

        plusImg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int value = Integer.valueOf(numTv.getText().toString());
                value++;
                numTv.setText(String.valueOf(value));
                numView.setBadgeCount(value);
            }
        });

        subImg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int value = Integer.valueOf(numTv.getText().toString());
                if (value > 1) {
                    value--;
                }
                numTv.setText(String.valueOf(value));
                numView.setBadgeCount(value);
            }
        });

这里简单模拟下购物车添加商品时,车内商品数量的变化。BadgeView作为Github上有名的开源项目,代码就不再这里贴出。


好了,widget的第一次总结就先到这里。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,522评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,601评论 18 139
  • 昨天看到敏妈发的两篇文章,看完想哭,着实感动。 她真的做到了对自己热爱的事情付出超于常人的努力。 是我学习的榜样!...
    小小向日葵up阅读 249评论 0 0
  • 文/Nichole 营销/公关 中国无IP。当张总这么跟我说的时候,我第一时间反应:太傲慢了!太偏见了!第二时间,...
    翁萍阅读 291评论 0 0
  • 当我一直在苦苦追寻和谐亲密关系的途中,我发现自己深深地陷入了受害者、迫害者、拯救者、牺牲者的角色里。这个追寻的过程...
    米勒Li阅读 322评论 1 2