zoukankan      html  css  js  c++  java
  • 恭喜发财! -- 手把手教你仿造一个qq下拉抢红包 Android自定义view

    猴年猴赛雷啊各位,今天没吃药我感觉自己萌萌哒!

    qq和微信和支付宝红包大战,不知道各位的战绩是多少嘞? 反正我qq抢到的都是气泡。因为太不爽,所以自己写一个下拉抢红包自己玩(自己跟自己玩)。

    先来看效果图。这个…… 呃~~ -__-” 。。有点丑 是低仿。
    这里写图片描述

    转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50662592

    学习完本篇博客你能获得到的知识

    1. 正确的获得view的大小
    2. listview的下拉header
    3. 自定义字体
    4. 自己添加监听器

    废话不多说,快跟我来一起动手

    • 首先来跟我打造一个自定义的listview

    新建一个类,继承自listview,这里需要来重写一下他的overScrollBy()方法。

    public class HBListView extends ListView {
    
        //header显示的图片
        private MyImageView mImageView;
        private Context mContext;
        //抢到红包时候的监听器
        private OnSuccessListener mOnSuccessListener;
    
    
        @Override
        protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
            Log.e("wing", "deltaY:" + deltaY + " scrollY:" + scrollY);
            return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
    
        }

    先来介绍一下重要参数的意思

    deltaX,Y是指速度值,也就是你手指滑动的瞬时速度。
    scrollX,Y 水平和数值方向上的变化量
    isTouchEvent 表示手指是否在触摸状态

    观察log可以轻易看到他们与手机触摸的关系。大家自己试验一下就可以。

    接着给listview加一个header

    mListView = (HBListView) findViewById(R.id.lv);
    
            final View header = LayoutInflater.from(this).inflate(R.layout.view_header,null);
            mListView.addHeaderView(header);

    其实这个header就是个图片。 我们刚才从listview的监听上面看到了偏移量。所以可以根据偏移量来动态改变图片的大小。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.wingsofts.hongbao.MyImageView
            android:id = "@+id/imageView"
            android:scaleType="centerCrop"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:src="@drawable/bg" />
    </LinearLayout>
    

    因为header刚开始是隐藏的,所以这里高度设置为0.

    这里来一个小插曲。。不知道大家有没有在activity中获取view的高度呢。是不是有时候只能拿到0。对于这个问题呢。正确的获取view宽高有如下几种解决方案。

    1.onWindowsFocusChanged()
    2.view.postRunnable() 将一个runnable投递到消息队列尾部。
    3.ViewTreeObserver

    这里采用第三种方法,在OnGlobalLayoutListener中 调用listview的changSize方法。。

    header.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    mListView.changeSize(image);
                    header.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                }
            });

    这里看看listview的changsize方法,其实只是将header的引用传来而已..这是因为之前在这里获取高度,后来又去掉了。。偷懒没有改方法名

       public void changeSize(MyImageView imageView) {
            mImageView = imageView;
        }

    拿到了图片之后,理所当然是根据手指来改变图片的大小啦。 还记得onScrollBy里面的几个参数吗,我们只需要在 (触摸的时候&&下滑的时候) 改变图片的大小就可以了。
    下滑的状态可以根据log得知为deltaY<0.

     @Override
        protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
            Log.e("wing", "deltaY:" + deltaY + " scrollY:" + scrollY);
    
            //给图片一个最大值
            if (mImageView.getHeight() < 300) {
                //是触摸状态 以及 是下滑状态
                if (isTouchEvent && deltaY < 0) {
                    //动态改变imageView的大小
                    mImageView.getLayoutParams().height += Math.abs(deltaY);
                    mImageView.requestLayout();
                }
            }
    
    
            return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
    
        }

    这样便可以下拉了,现在的效果是这样的。
    这里写图片描述

    咦。。是可以下拉了。但是没有回弹,怎么办!

    还能怎么办 写一个就是了。思路就是用一个动画,来不停地改变header高度。

    ObjectAnimator的使用

    他的使用方法很简单,他通过反射调用set方法来改变view的属性然后发生动画。这里直接上例子,首先重写一下imageview,添加一个setHeight()方法.

    public class MyImageView extends ImageView {
        private Paint mPaint;
        public void setHeight(int height) {
    
            getLayoutParams().height = height;
            requestLayout();
        }
    }
    

    然后在使用ObjectAnimator的ofInt方法,获取到一个ObjectAnimator,这里ofXXX具体看参数类型。

    //获取mImageView的setHeight方法,数值从当前图片的高度逐渐到0,产生动画
     private void closeHeader() {
    ObjectAnimator oa = ObjectAnimator.ofInt(mImageView, "height", mImageView.getHeight(), 0);
            oa.start();
            }

    这时候关闭动画就做完啦。想要关闭的时候 只需要在action_up调用closeHeader()方法即可.现在是这样的:
    这里写图片描述

     @Override
        public boolean onTouchEvent(MotionEvent ev) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_UP:
                        closeHeader();
                   break;
            }

    咦,有没有似曾相识的赶脚,没错!!这特么的不就是下拉刷新么。 好了本篇博客到此结束!!

    这里写图片描述

    额。。你先把手里的刀放下w(゚Д゚)w!!!
    刚才结束的是下拉刷新的博客,现在继续写抢红包的博客。。。

    思路就是用一个变量来保存连刷的次数,再用随机数判断是否抽中红包。
    给MyImageView一个public的属性次数

    public class MyImageView extends ImageView {
        public int mTime;
        }

    然后只要在ACTION_UP的时候,判断随机数有没有抽中,决定次数是否累加:

       @Override
        public boolean onTouchEvent(MotionEvent ev) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_UP:
    
                    int ran = (int) (Math.random() * 10);
                    //设置中奖概率
                    if (ran > 1) {
                        //如果没中次数累加
                        mImageView.mTime++;
                        closeHeader();
                    } else {
                        //否则为中奖 
                        mImageView.mTime = 0;
                        if (mOnSuccessListener != null) {
                            mOnSuccessListener.onSuccess();
                        }
                        closeHeader();
                    }
    
                    break;
            }
    
            return super.onTouchEvent(ev);
        }

    逻辑是不是粉简单! 聪明的你已经看到了抢红包成功的回调。这个等等说,先说拿到了次数以后怎么做。

    拿到了次数之后理所应道就是把文字画上去啦~~ 这里为了方便就给了一个固定的位置。因为数字大小跟文字不一样,所以把字符串分了三串来画..

      public int mTime;
        private Typeface mTypeFace;
        private float mTxtHLength;
        private float mTxtRLength;
    
        private String txtH = "连刷";
        private String txtR = "次,加油!";
    
    public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mPaint = new Paint();
            mPaint.setColor(Color.YELLOW);
            mMidPaint = new Paint();
            mMidPaint.setColor(Color.YELLOW);
            mTypeFace = Typeface.createFromAsset(context.getAssets(), "ifont.ttf");
            mMidPaint.setTypeface(mTypeFace);
            mPaint.setTypeface(mTypeFace);
            //设置文字大小
            mPaint.setTextSize(50);
            //设置数字大小
            mMidPaint.setTextSize(100);
    
            //测量头文字的长度
            mTxtHLength = mPaint.measureText(txtH);
            //测量尾文字的长度
            mTxtRLength = mPaint.measureText(txtR);
        }
       @Override
        protected void onDraw(Canvas canvas) {
    
            super.onDraw(canvas);
            String count = mTime + "";
            //数字的长度
            float countLength = mMidPaint.measureText(count);
            //总体长度
            float totalLength = mTxtRLength + countLength;
    
            //画在正中间
            float start = getMeasuredWidth() / 2 - totalLength / 2;
    
            canvas.drawText(txtH, 100, getMeasuredHeight() / 2, mPaint);
            canvas.drawText(count, start + countLength / 2, getMeasuredHeight() / 2, mMidPaint);
            canvas.drawText(txtR, start + countLength / 2 + mTxtRLength / 2, getMeasuredHeight() / 2, mPaint);
        }

    这样就把次数提示画上去了!来看看效果:
    这里写图片描述
    这个…… 呃~~ -__-” 是画上去了,但是

    字!体!好!丑!

    没事没事,别着急。。我们换一个字体就是了。

    Paint字体的改变

    1.首先下载一个字体 放到assets文件夹下
    2.获取到字体的引用
    3.给画笔设置字体

            //改变字体,就是这么轻松自如~~
            mTypeFace = Typeface.createFromAsset(context.getAssets(), "ifont.ttf");
            mMidPaint.setTypeface(mTypeFace);
            mPaint.setTypeface(mTypeFace);

    看效果图:
    这里写图片描述

    吼吼吼~~~ 有点感觉了。

    监听器的添加

    先来回过头来,大家知道View有setOnClickListener.. 那么我们也来加一个红包成功监听器吧。
    在ListView里面增加一个内部接口,添加一个set方法

    
         private OnSuccessListener mOnSuccessListener;
    
         interface OnSuccessListener {
            void onSuccess();
        }
    
        public void setOnSuccessListener(OnSuccessListener onSuccessListener) {
            mOnSuccessListener = onSuccessListener;
        }

    在ACTION_UP成果逻辑里面调用onSuccess()… 这就是监听器。。简单吧~就是个回调

    现在在MainActivity里使用这个抢红包listView吧~
    设置一个监听器,里面写你想要的事件,中几千万的随便写,总比气泡好,,

     mListView.setAdapter(new ArrayAdapter(this,android.R.layout.simple_list_item_1,datas));
            mListView.setOnSuccessListener(new HBListView.OnSuccessListener() {
                @Override
                public void onSuccess() {
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    builder.setMessage("恭喜中奖!抽到了疼逊聊天气泡!").setNegativeButton("确认", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
    
                        }
                    }).show();
                }
            });

    这样就能出现文章顶部预览图的效果了:

    这里写图片描述

    如果你喜欢我的博客,请继续关注我,求赞求顶~求吐槽

    本项目地址:打开链接

    ———————————–华丽丽的分割线————————————

    效果优化

    处于追求完美的心态,群友 小情歌 对代码做了一些优化,主要有

    1.修复上滑也显示红包的bug
    2.优化动画事件,在松手的过程中如果继续ACTION_DOWN则放弃动画
    3.增加视差特效

    修改其实也不复杂 具体看这一段代码:

     @Override
        public boolean onTouchEvent(MotionEvent ev) {
    
            switch (ev.getAction()) {
    
                case MotionEvent.ACTION_DOWN:
                    x1 = ev.getX();
                    y1 = ev.getY();
    
                    if (oa != null) {
                        if (oa.isRunning()) {
                            oa.cancel();
                        }
                    }
    
                    if (mImageView.getHeight() == 0) {
                        mImageView.mTime = 0;
                    }
                    break;
    
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
    
                    x2 = ev.getX();
                    y2 = ev.getY();
                    if (y1 - y2 > 0) {
                        b = false;
                    } else if (y2 - y1 > 0) {
                        b = true;
                    }
    
                    int ran = (int) (Math.random() * 100);
                    Log.e("wing", ran + "");
                    if (b) {  //往下滑
    
                        if (ran > 3) {
                            mImageView.mTime++;
                            closeHeader();
                        } else {
                            mImageView.mTime = 0;
                            if (mOnSuccessListener != null) {
                                mOnSuccessListener.onSuccess();
                            }
                            closeHeader();
                        }
                    }
    
                    break;
            }
            return super.onTouchEvent(ev);
        }
    

    相信大家都看得懂,这里就不一一说明了。

  • 相关阅读:
    [Design]设计模式结构模式
    [Design] 设计模式行为模式
    [Design] Decorator Pattern
    ILIST<T>和LIST<T> 枫
    js 如何调用Windows自带的配色控件 枫
    WML语法全接触 WAP建站语言 枫
    Asp.net模板引擎技术 枫
    smarty内建函数 枫
    NameValueCollection详解 枫
    smarty循环调用问题 枫
  • 原文地址:https://www.cnblogs.com/muyuge/p/6333532.html
Copyright © 2011-2022 走看看