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的第一次总结就先到这里。

发布了47 篇原创文章 · 获赞 23 · 访问量 8万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章