(原创)自定义view(view的绘制过程)、无限轮播并触碰停止轮播的viewpage、水平和垂直滚动的TextView、仿QQ滑动删除、下拉刷新上拉加载view、毛玻璃效果、低版本水波纹、圆环头像图

1.前言

最近有朋友问我些自定义控件的一些问题,又问我关于无限轮播viewpager的实现方式,有空之余就编写篇blog来讲解下吧,希望对你们有帮助。本篇文章只要讲解些开发中常用的一些自定义控件view的demo,本人酷爱cs但工作之后就好几年没碰了,就用手游枪击作为图片demo做为缅怀(*^-^*)。文章最后附带demo下载链接地址。

文章总体实现无限轮播并触碰停止轮播的viewpage、水平和垂直滚动的TextView、仿QQ滑动删除、下拉刷新上拉加载view、毛玻璃效果、低版本水波纹、圆环头像图片。

View的绘制过程图:

View的事件分发机制图:

2.无限轮播viewPager

效果图:


实现代码:

package com.example.lainanzhou.customviewdemo.activity;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.example.lainanzhou.customviewdemo.R;
import com.example.lainanzhou.customviewdemo.util.DimensUtil;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * 无限轮播的Viewpager
 * <p/>
 * Created by Joker on 2016/6/30.
 */
public class ViewPagerActivity extends Activity implements ViewPager.OnPageChangeListener {
    @BindView(R.id.viewPager)
    ViewPager mViewPager;
    @BindView(R.id.picture_container)
    LinearLayout mPictureContainer;
    private int[] mPicturesId = {R.mipmap.one, R.mipmap.tow, R.mipmap.three, R.mipmap.four, R.mipmap.five, R.mipmap.six};
    private Handler mMainHandler;
    private AutoSwitchTask mSwitchTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_viewpager);
        ButterKnife.bind(this);
        initData();
        initEvent();
    }

    private void initData() {
        mViewPager.setAdapter(new PictureAdapter());
        mMainHandler = new Handler(getMainLooper());
    }

    private void initEvent() {
        // 设置viewpager的监听
        mViewPager.setOnPageChangeListener(this);

        // 设置ViewPager中间页(避免从0开始角标越界)
        int middle = Integer.MAX_VALUE / 2;
        int extra = middle % mPicturesId.length;
        mViewPager.setCurrentItem(middle - extra);

        // 给容器添加点
        addPictureContainer();
        //开启自动轮播任务
        startAutoSwitchTask();

    }

    private void startAutoSwitchTask() {
        // 开始轮播任务
        if (mSwitchTask == null) {
            mSwitchTask = new AutoSwitchTask();
        }
        mSwitchTask.start();

        // 给ViewPager设置touch的监听:但手指触摸时停止轮播页面
        mViewPager.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        //停止轮播
                        mSwitchTask.stop();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        break;
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_CANCEL:
                        //开启轮播
                        mSwitchTask.start();
                        break;
                    default:
                        break;
                }
                return false;
            }
        });
    }

    class AutoSwitchTask implements Runnable {
        //开始轮播
        public void start() {
            stop();
            mMainHandler.postDelayed(this, 3000);
        }

        //停止轮播
        public void stop() {
            mMainHandler.removeCallbacks(this);
        }

        @Override
        public void run() {
            int item = mViewPager.getCurrentItem();
            mViewPager.setCurrentItem(++item);

            mMainHandler.postDelayed(this, 3000);
        }

    }

    //添加点
    private void addPictureContainer() {
        mPictureContainer.removeAllViews();
        for (int i = 0; i < mPicturesId.length; i++) {
            View view = new View(this);
            view.setBackgroundResource(R.mipmap.indicator_normal);

            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(DimensUtil.dip2px(6), DimensUtil.dip2px(6));
            if (i != 0) {
                params.leftMargin = DimensUtil.dip2px(8);
            } else {
                view.setBackgroundResource(R.mipmap.indicator_selected);// 设置默认选中
            }
            mPictureContainer.addView(view, params);
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    /**
     * 页面给选中时回调
     *
     * @param position
     */
    @Override
    public void onPageSelected(int position) {
        position = position % mPicturesId.length;

        int count = mPictureContainer.getChildCount();
        for (int i = 0; i < count; i++) {
            View view = mPictureContainer.getChildAt(i);
            view.setBackgroundResource(i == position ? R.mipmap.indicator_selected
                    : R.mipmap.indicator_normal);
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }

    private class PictureAdapter extends PagerAdapter {
        /**
         * 设置最大页面数量,实现无限轮播
         *
         * @return
         */
        @Override
        public int getCount() {
            return Integer.MAX_VALUE;
        }

        /**
         * 复用view对象
         *
         * @param view
         * @param object
         * @return
         */
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        /**
         * 初始化显示的view
         *
         * @param container
         * @param position
         * @return
         */
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            position = position % mPicturesId.length;
            ImageView iv = new ImageView(ViewPagerActivity.this);
            iv.setScaleType(ImageView.ScaleType.FIT_XY);
            iv.setImageResource(mPicturesId[position]);
            container.addView(iv);

            return iv;
        }

        /**
         * 回收view
         *
         * @param container
         * @param position
         * @param object
         */
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }
    }
}


3.水平滚动和垂直滚动的TextView的实现

效果图:



实现代码:

package com.example.lainanzhou.customviewdemo.activity;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import com.example.lainanzhou.customviewdemo.R;
import com.example.lainanzhou.customviewdemo.view.MarqueeTextView;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * TODO:
 * 水平和垂直滚动的TextView
 *
 * @author Joker
 * @createDate 2016/7/5.
 */
public class MarqueeTextViewActivity extends Activity {
    @BindView(R.id.marqueeTextView1)
    MarqueeTextView mMarqueeTextView1;
    @BindView(R.id.marqueeTextView2)
    MarqueeTextView mMarqueeTextView2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_marqueetextview);
        ButterKnife.bind(this);
        initEvent();
    }

    private void initEvent() {
        setMarqueeTextView1();
        setMarqueeTextView2();

    }

    private void setMarqueeTextView2() {
        ViewGroup.MarginLayoutParams margin = new ViewGroup.MarginLayoutParams(
                mMarqueeTextView2.getLayoutParams());
        margin.setMargins(50, 50, 50, 0);//设置滚动区域位置
        //测量marqueeTextView宽高,在没有展示之前获取宽高
        int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        mMarqueeTextView2.measure(w, h);
        int height = mMarqueeTextView2.getMeasuredHeight();
        int width = mMarqueeTextView2.getMeasuredWidth();
        //必须要设置布局,不然没法显示
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(margin);
        layoutParams.height = height;
        layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
        mMarqueeTextView2.setLayoutParams(layoutParams);
        mMarqueeTextView2.setScrollWidth(1000);
        mMarqueeTextView2.setScrollHeight(height);
        mMarqueeTextView2.setCurrentPosition(0);//设置滚动信息从滚动区域的右边出来
        mMarqueeTextView2.setSpeed(2);
        mMarqueeTextView2.setText("这才是真正的垂直跑马灯效果!");
        mMarqueeTextView2.setBackgroundColor(Color.parseColor("#dddddd"));
    }

    private void setMarqueeTextView1() {
        ViewGroup.MarginLayoutParams margin = new ViewGroup.MarginLayoutParams(
                mMarqueeTextView1.getLayoutParams());
        margin.setMargins(50, 50, 50, 0);//设置滚动区域位置
        //测量marqueeTextView宽高,在没有展示之前获取宽高
        int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        mMarqueeTextView1.measure(w, h);
        int height = mMarqueeTextView1.getMeasuredHeight();
        int width = mMarqueeTextView1.getMeasuredWidth();
        //必须要设置布局,不然没法显示
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(margin);
        layoutParams.height = height;
        layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
        mMarqueeTextView1.setLayoutParams(layoutParams);
        mMarqueeTextView1.setScrollWidth(1000);
        mMarqueeTextView1.setScrollHeight(height);
        mMarqueeTextView1.setCurrentPosition(0);//设置滚动信息从滚动区域的右边出来
        mMarqueeTextView1.setSpeed(2);
        mMarqueeTextView1.setText("这才是真正的水平跑马灯效果!");
        mMarqueeTextView1.setBackgroundColor(Color.parseColor("#dddddd"));
    }
}

4.仿QQ滑动删除 

效果图:


实现代码:

package com.example.lainanzhou.customviewdemo.activity;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

import com.example.lainanzhou.customviewdemo.R;
import com.example.lainanzhou.customviewdemo.view.SlipView;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * TODO:
 * 滑动删除item
 *
 * @author Joker
 * @createDate 2016/7/5.
 */
public class SlipViewActivity extends Activity implements AbsListView.OnScrollListener {
    @BindView(R.id.listView)
    ListView mListView;
    private List<String> mDatas = new ArrayList<>();
    //记录打开的view
    private List<SlipView> mOpenedViews = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_slipview);
        ButterKnife.bind(this);
        initData();
    }

    private void initData() {
        for (int i = 0; i < 50; i++) {
            mDatas.add(" 内容--" + i);
        }
        // 设置adapter
        mListView.setAdapter(new MyAdapter());
        // 设置listView的滑动监听
        mListView.setOnScrollListener(this);
    }


    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING
                || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
            closeOpenedView();
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

    }


    private class MyAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return mDatas == null ? 0 : mDatas.size();
        }

        @Override
        public Object getItem(int position) {
            return mDatas == null ? null : mDatas.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if (convertView == null) {
                holder = new ViewHolder();
                convertView = View.inflate(SlipViewActivity.this, R.layout.item,
                        null);
                holder.sv = (SlipView) convertView.findViewById(R.id.sv);
                holder.tv = (TextView) convertView.findViewById(R.id.tv);
                holder.delete = convertView.findViewById(R.id.delete);
                convertView.setTag(holder);

            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            holder.tv.setText(mDatas.get(position));

            // 监听sweepView
            holder.sv.setOnSweepListener(new SlipView.OnSweepListener() {

                @Override
                public void onOpen(SlipView view) {
                    // 先关闭已经打开的View
                    closeOpenedView();
                    synchronized (mOpenedViews) {
                        mOpenedViews.add(view);
                    }
                }

                @Override
                public void onClose(SlipView view) {
                    synchronized (mOpenedViews) {
                        mOpenedViews.remove(view);
                    }
                }
            });

            holder.delete.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mDatas.remove(position);
                    notifyDataSetChanged();
                }
            });
            return convertView;
        }
    }

    private void closeOpenedView() {
        ListIterator<SlipView> iterator = mOpenedViews.listIterator();
        while (iterator.hasNext()) {
            SlipView next = iterator.next();
            next.close();
        }
    }

    class ViewHolder {
        SlipView sv;
        TextView tv;
        View delete;
    }
}

5.下拉刷新上拉加载更多

效果图:


代码实现 :
(1).没携带头view的RefreshListView

package com.example.lainanzhou.customviewdemo.activity;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.example.lainanzhou.customviewdemo.R;
import com.example.lainanzhou.customviewdemo.view.RefreshListView;

import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * TODO:
 * 实现自定义下拉刷新和加载更多的activity
 *
 * @author Joker
 * @createDate 2016/7/5.
 */
public class RefreshListViewActivity extends Activity implements RefreshListView.OnRefreshListener {
    @BindView(R.id.refreshlistview)
    RefreshListView mRefreshlistview;
    private List<String> textList = new ArrayList<>();
    private MyAdapter adapter;
    private int count;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_refreshlistview);
        ButterKnife.bind(this);
        initData();
    }

    private void initData() {
        for (int i = 0; i < 30; i++) {
            textList.add("内容" + i);
        }

        adapter = new MyAdapter();
        mRefreshlistview.setAdapter(adapter);

        mRefreshlistview.setOnRefreshListener(this);
    }

    public void click(View view){
        Intent intent  = new Intent(this,RefreshListViewActivity2.class);
        startActivity(intent);
    }

    @Override
    public void onDownPullRefresh() {//下拉刷新回调
        new AsyncTask<String, Integer, Void>() {

            @Override
            protected Void doInBackground(String... params) {
                SystemClock.sleep(2000);
                count = 0;
                textList.clear();
                for (int i = 0; i < 30; i++) {
                    textList.add("内容" + i);
                }
                return null;
            }

            @Override
            protected void onPostExecute(Void result) {
                adapter.notifyDataSetChanged();
                mRefreshlistview.hideHeaderView();
            }
        }.execute(new String[]{});
    }

    @Override
    public void onLoadingMore() {//上拉加载更多回调
        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                SystemClock.sleep(2000);
                textList.add("添加内容" + (++count));
                textList.add("添加内容" + (++count));
                textList.add("添加内容" + (++count));
                textList.add("添加内容" + (++count));
                textList.add("添加内容" + (++count));
                return null;
            }

            @Override
            protected void onPostExecute(Void result) {
                adapter.notifyDataSetChanged();
                mRefreshlistview.hideFooterView();
            }
        }.execute(new Void[]{});
    }

    class MyAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            return textList.size();
        }

        @Override
        public Object getItem(int arg0) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            TextView tv = new TextView(RefreshListViewActivity.this);
            tv.setText(textList.get(position));
            tv.setTextSize(18);
            tv.setTextColor(Color.BLACK);
            return tv;
        }

    }
}

(2).携带头 View的RefreshListView

package com.example.lainanzhou.customviewdemo.activity;

import android.app.Activity;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.example.lainanzhou.customviewdemo.R;
import com.example.lainanzhou.customviewdemo.view.RefreshListView2;

import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * TODO:
 * 第二种自定义携带头布局的下拉刷新上拉加载更多的ListView
 *
 * @author Joker
 * @createDate 2016/7/5.
 */
public class RefreshListViewActivity2 extends Activity implements RefreshListView2.OnRefreshListener, AdapterView.OnItemClickListener {
    @BindView(R.id.refreshlistview2)
    RefreshListView2 mRefreshlistview2;
    private List<String> textList = new ArrayList<>();
    private ListDataAdapter mTextAdapter;
    private int count;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_refreshlistview2);
        ButterKnife.bind(this);
        initData();
        initEvent();
    }

    private void initEvent() {
        // 给ListView加载自定义的头布局
        TextView hearView = new TextView(this);
        hearView.setText("我是头view");
        hearView.setTextSize(24);
        hearView.setTextColor(Color.parseColor("#ff0000"));
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams
                (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        hearView.setLayoutParams(params);
        mRefreshlistview2.addCustomHeaderView(hearView);

        // 设置刷新监听
        mRefreshlistview2.setOnRefreshListener(this);

        // 设置item的监听
        mRefreshlistview2.setOnItemClickListener(this);
    }

    private void initData() {
        for (int i = 0; i < 30; i++) {
            textList.add("内容" + i);
        }
        mTextAdapter = new ListDataAdapter();
        mRefreshlistview2.setAdapter(mTextAdapter);
    }

    @Override
    public void onRefreshing() {
        new AsyncTask<String, Integer, Void>() {

            @Override
            protected Void doInBackground(String... params) {
                SystemClock.sleep(2000);
                count = 0;
                textList.clear();
                for (int i = 0; i < 30; i++) {
                    textList.add("内容" + i);
                }
                return null;
            }

            @Override
            protected void onPostExecute(Void result) {
                mRefreshlistview2.setRereshTime(System.currentTimeMillis());
                mTextAdapter.notifyDataSetChanged();
                mRefreshlistview2.setRefreshFinish();
            }
        }.execute(new String[]{});
    }

    @Override
    public void onLoadMore() {
        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                SystemClock.sleep(2000);
                textList.add("添加内容" + (++count));
                textList.add("添加内容" + (++count));
                textList.add("添加内容" + (++count));
                textList.add("添加内容" + (++count));
                textList.add("添加内容" + (++count));
                return null;
            }

            @Override
            protected void onPostExecute(Void result) {
                mTextAdapter.notifyDataSetChanged();
                mRefreshlistview2.setRefreshFinish();
            }
        }.execute(new Void[]{});
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

    }

    class ListDataAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            return textList.size();
        }

        @Override
        public Object getItem(int position) {
            return textList.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            TextView tv = new TextView(RefreshListViewActivity2.this);
            tv.setText(textList.get(position));
            tv.setTextSize(18);
            tv.setTextColor(Color.BLACK);
            return tv;
        }
    }
}

6.仿苹果毛玻璃效果

效果图:



实现代码:

package com.example.lainanzhou.customviewdemo.activity;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;

import com.example.lainanzhou.customviewdemo.R;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * TODO:
 * 展示毛玻璃效果的activity
 * 1.针对API16以上可以使用Android自带api RenderScript去实现
 * 2.对于api16以下的适配,不过性能会比较低
 * 3.使用c语言实现:使用了jni方式调用实现
 * <p/>
 * github上有个开源框架挺好的,地址:https://github.com/kikoso/android-stackblur
 * 另项目中附上,其实就是使用jni调用c实现
 *
 * @author Joker
 * @createDate 2016/7/5.
 */
public class BlurViewActivity extends Activity {
    @BindView(R.id.sb)
    SeekBar mSb;
    @BindView(R.id.iv)
    ImageView mIv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_blurview);
        ButterKnife.bind(this);
        initData();
    }

    private void initData() {
        mSb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                onBlur();
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }

    private void onBlur() {
        if (mSb.getProgress() <= 0 || mSb.getProgress() >= 25)
            return;
        int radius = mSb.getProgress();
        Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), R.mipmap.android_platform_256);
        mIv.setImageBitmap(getBlurBitmap(this, bitmap, radius));
        //        mIv.setImageBitmap(fastblur(this, bitmap, radius));
    }

    public void click2GitHub(View view) {
        Intent intent = new Intent(this, BlurViewFromGithubActivity.class);
        startActivity(intent);
    }

    /**
     * @param context
     * @param sentBitmap
     * @param radius     模糊半径(模糊度)不能小于0和大于25
     * @return
     */
    private Bitmap getBlurBitmap(Context context, Bitmap sentBitmap, int radius) {
        //针对api16以上的处理方式
        if (Build.VERSION.SDK_INT > 16) {
            Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
            final RenderScript rs = RenderScript.create(context);
            final Allocation input = Allocation.createFromBitmap(rs, sentBitmap, Allocation.MipmapControl.MIPMAP_NONE,
                    Allocation.USAGE_SCRIPT);
            final Allocation output = Allocation.createTyped(rs, input.getType());
            final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
            script.setRadius(radius);//e.g. 3.f
            script.setInput(input);
            script.forEach(output);
            output.copyTo(bitmap);
            return bitmap;
        }
        return fastblur(context, sentBitmap, radius);
    }

    /**
     * 针对api16以下的处理方式
     *
     * @param context
     * @param sentBitmap
     * @param radius
     * @return
     */
    public Bitmap fastblur(Context context, Bitmap sentBitmap, int radius) {

        Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);

        if (radius < 1) {
            return (null);
        }

        int w = bitmap.getWidth();
        int h = bitmap.getHeight();

        int[] pix = new int[w * h];
        bitmap.getPixels(pix, 0, w, 0, 0, w, h);

        int wm = w - 1;
        int hm = h - 1;
        int wh = w * h;
        int div = radius + radius + 1;

        int r[] = new int[wh];
        int g[] = new int[wh];
        int b[] = new int[wh];
        int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
        int vmin[] = new int[Math.max(w, h)];

        int divsum = (div + 1) >> 1;
        divsum *= divsum;
        int temp = 256 * divsum;
        int dv[] = new int[temp];
        for (i = 0; i < temp; i++) {
            dv[i] = (i / divsum);
        }

        yw = yi = 0;

        int[][] stack = new int[div][3];
        int stackpointer;
        int stackstart;
        int[] sir;
        int rbs;
        int r1 = radius + 1;
        int routsum, goutsum, boutsum;
        int rinsum, ginsum, binsum;

        for (y = 0; y < h; y++) {
            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
            for (i = -radius; i <= radius; i++) {
                p = pix[yi + Math.min(wm, Math.max(i, 0))];
                sir = stack[i + radius];
                sir[0] = (p & 0xff0000) >> 16;
                sir[1] = (p & 0x00ff00) >> 8;
                sir[2] = (p & 0x0000ff);
                rbs = r1 - Math.abs(i);
                rsum += sir[0] * rbs;
                gsum += sir[1] * rbs;
                bsum += sir[2] * rbs;
                if (i > 0) {
                    rinsum += sir[0];
                    ginsum += sir[1];
                    binsum += sir[2];
                } else {
                    routsum += sir[0];
                    goutsum += sir[1];
                    boutsum += sir[2];
                }
            }
            stackpointer = radius;

            for (x = 0; x < w; x++) {

                r[yi] = dv[rsum];
                g[yi] = dv[gsum];
                b[yi] = dv[bsum];

                rsum -= routsum;
                gsum -= goutsum;
                bsum -= boutsum;

                stackstart = stackpointer - radius + div;
                sir = stack[stackstart % div];

                routsum -= sir[0];
                goutsum -= sir[1];
                boutsum -= sir[2];

                if (y == 0) {
                    vmin[x] = Math.min(x + radius + 1, wm);
                }
                p = pix[yw + vmin[x]];

                sir[0] = (p & 0xff0000) >> 16;
                sir[1] = (p & 0x00ff00) >> 8;
                sir[2] = (p & 0x0000ff);

                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];

                rsum += rinsum;
                gsum += ginsum;
                bsum += binsum;

                stackpointer = (stackpointer + 1) % div;
                sir = stack[(stackpointer) % div];

                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];

                rinsum -= sir[0];
                ginsum -= sir[1];
                binsum -= sir[2];

                yi++;
            }
            yw += w;
        }
        for (x = 0; x < w; x++) {
            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
            yp = -radius * w;
            for (i = -radius; i <= radius; i++) {
                yi = Math.max(0, yp) + x;

                sir = stack[i + radius];

                sir[0] = r[yi];
                sir[1] = g[yi];
                sir[2] = b[yi];

                rbs = r1 - Math.abs(i);

                rsum += r[yi] * rbs;
                gsum += g[yi] * rbs;
                bsum += b[yi] * rbs;

                if (i > 0) {
                    rinsum += sir[0];
                    ginsum += sir[1];
                    binsum += sir[2];
                } else {
                    routsum += sir[0];
                    goutsum += sir[1];
                    boutsum += sir[2];
                }

                if (i < hm) {
                    yp += w;
                }
            }
            yi = x;
            stackpointer = radius;
            for (y = 0; y < h; y++) {
                pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16)
                        | (dv[gsum] << 8) | dv[bsum];

                rsum -= routsum;
                gsum -= goutsum;
                bsum -= boutsum;

                stackstart = stackpointer - radius + div;
                sir = stack[stackstart % div];

                routsum -= sir[0];
                goutsum -= sir[1];
                boutsum -= sir[2];

                if (x == 0) {
                    vmin[y] = Math.min(y + r1, hm) * w;
                }
                p = x + vmin[y];

                sir[0] = r[p];
                sir[1] = g[p];
                sir[2] = b[p];

                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];

                rsum += rinsum;
                gsum += ginsum;
                bsum += binsum;

                stackpointer = (stackpointer + 1) % div;
                sir = stack[stackpointer];

                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];

                rinsum -= sir[0];
                ginsum -= sir[1];
                binsum -= sir[2];

                yi += w;
            }
        }

        bitmap.setPixels(pix, 0, w, 0, 0, w, h);
        return (bitmap);
    }
}

7.适配低版本的水波纹

效果图:


代码实现:
(1).继承LinearLayout实现

package com.example.lainanzhou.customviewdemo.view;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;

import com.example.lainanzhou.customviewdemo.R;

import java.util.ArrayList;

/**
 * TODO:
 * 自定义继承LinearLayout的水波纹效果view
 * 注意:
 * 该方式实现的水波纹无法在listView中使用,会无法实现listView里面item点击事件,因为拦截掉了
 * 且该方式实现方式是作为父容器将所有的子控件都包裹在里面,不然会出现点击处理逻辑错乱
 * 针对这几点缺点,后面又搞了个WaterRelativeLayout进行适配listView
 *
 * @author Joker
 * @createDate 2016/7/6.
 */
public class WaterLinearLayout extends LinearLayout implements Runnable {
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private int mTargetWidth;
    private int mTargetHeight;
    private int mMinBetweenWidthAndHeight;
    private int mMaxBetweenWidthAndHeight;
    private int mMaxRevealRadius;
    private int mRevealRadiusGap;
    private int mRevealRadius = 0;
    private float mCenterX;
    private float mCenterY;
    private int[] mLocationInScreen = new int[2];
    private boolean mShouldDoAnimation = false;
    private boolean mIsPressed = false;
    private int INVALIDATE_DURATION = 40;//播放水波纹持续时间

    private View mTouchTarget;
    private DispatchUpTouchEventRunnable mDispatchUpTouchEventRunnable = new DispatchUpTouchEventRunnable();

    public WaterLinearLayout(Context context) {
        super(context);
        init();
    }

    public WaterLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public WaterLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setWillNotDraw(false);
        mPaint.setColor(getResources().getColor(R.color.water_color));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        this.getLocationOnScreen(mLocationInScreen);
    }

    private void initParametersForChild(MotionEvent event, View view) {
        mCenterX = event.getX();
        mCenterY = event.getY();
        mTargetWidth = view.getMeasuredWidth();
        mTargetHeight = view.getMeasuredHeight();
        mMinBetweenWidthAndHeight = Math.min(mTargetWidth, mTargetHeight);
        mMaxBetweenWidthAndHeight = Math.max(mTargetWidth, mTargetHeight);
        mRevealRadius = 0;
        mShouldDoAnimation = true;
        mIsPressed = true;
        mRevealRadiusGap = mMinBetweenWidthAndHeight / 8;

        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int left = location[0] - mLocationInScreen[0];
        int transformedCenterX = (int) mCenterX - left;
        mMaxRevealRadius = Math.max(transformedCenterX, mTargetWidth - transformedCenterX);
    }

    /**
     * 实现绘制圆动画
     *
     * @param canvas
     */
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (!mShouldDoAnimation || mTargetWidth <= 0 || mTouchTarget == null) {
            return;
        }

        if (mRevealRadius > mMinBetweenWidthAndHeight / 2) {
            mRevealRadius += mRevealRadiusGap * 4;
        } else {
            mRevealRadius += mRevealRadiusGap;
        }
        this.getLocationOnScreen(mLocationInScreen);
        int[] location = new int[2];
        mTouchTarget.getLocationOnScreen(location);
        int left = location[0] - mLocationInScreen[0];
        int top = location[1] - mLocationInScreen[1];
        int right = left + mTouchTarget.getMeasuredWidth();
        int bottom = top + mTouchTarget.getMeasuredHeight();

        canvas.save();
        canvas.clipRect(left, top, right, bottom);
        canvas.drawCircle(mCenterX, mCenterY, mRevealRadius, mPaint);
        canvas.restore();

        if (mRevealRadius <= mMaxRevealRadius) {
            //postInvalidate是在非UI线程中更新UI的方法
            postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);
        } else if (!mIsPressed) {
            mShouldDoAnimation = false;
            postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);
        }
    }

    /**
     * 拦截处理touch事件
     *
     * @param event
     * @return
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();
        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            View touchTarget = getTouchTarget(this, x, y);
            if (touchTarget != null && touchTarget.isClickable() && touchTarget.isEnabled()) {
                mTouchTarget = touchTarget;
                initParametersForChild(event, touchTarget);
                postInvalidateDelayed(INVALIDATE_DURATION);
            }
        } else if (action == MotionEvent.ACTION_UP) {
            mIsPressed = false;
            postInvalidateDelayed(INVALIDATE_DURATION);
            mDispatchUpTouchEventRunnable.event = event;
            postDelayed(mDispatchUpTouchEventRunnable, 40);
            return true;
        } else if (action == MotionEvent.ACTION_CANCEL) {
            mIsPressed = false;
            postInvalidateDelayed(INVALIDATE_DURATION);
        }

        return super.dispatchTouchEvent(event);
    }

    private View getTouchTarget(View view, int x, int y) {
        View target = null;
        ArrayList<View> TouchableViews = view.getTouchables();
        for (View child : TouchableViews) {
            if (isTouchPointInView(child, x, y)) {
                target = child;
                break;
            }
        }

        return target;
    }

    /**
     * 判断子控件是否可点击和获取点击子控件的坐标点
     *
     * @param view
     * @param x
     * @param y
     * @return
     */
    private boolean isTouchPointInView(View view, int x, int y) {
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int left = location[0];
        int top = location[1];
        int right = left + view.getMeasuredWidth();
        int bottom = top + view.getMeasuredHeight();
        if (view.isClickable() && y >= top && y <= bottom
                && x >= left && x <= right) {
            return true;
        }
        return false;
    }

    @Override
    public boolean performClick() {
        postDelayed(this, 400);
        return true;
    }

    @Override
    public void run() {
        super.performClick();
    }

    private class DispatchUpTouchEventRunnable implements Runnable {
        public MotionEvent event;

        @Override
        public void run() {
            if (mTouchTarget == null || !mTouchTarget.isEnabled()) {
                return;
            }

            if (isTouchPointInView(mTouchTarget, (int) event.getRawX(), (int) event.getRawY())) {
                mTouchTarget.performClick();//分发处理点击子控件view的事件
            }
        }
    }
}

(2).继承RelateLayout实现 

package com.example.lainanzhou.customviewdemo.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.ColorRes;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
import android.widget.RelativeLayout;

import com.example.lainanzhou.customviewdemo.R;

/**
 * TODO:
 * 继承RelativeLayout实现自定义水波纹效果的view
 * 可以作为listView里面的item的父容器使用
 *
 * @author Joker
 * @createDate 2016/7/6.
 */
public class WaterRelativeLayout extends RelativeLayout {

    private int WIDTH;
    private int HEIGHT;
    private int frameRate = 10;
    private int rippleDuration = 400;
    private int rippleAlpha = 90;
    private Handler canvasHandler;
    private float radiusMax = 0;
    private boolean animationRunning = false;
    private int timer = 0;
    private int timerEmpty = 0;
    private int durationEmpty = -1;
    private float x = -1;
    private float y = -1;
    private int zoomDuration;
    private float zoomScale;
    private ScaleAnimation scaleAnimation;
    private Boolean hasToZoom;
    private Boolean isCentered;
    private Integer rippleType;
    private Paint paint;
    private Bitmap originBitmap;
    private int rippleColor;
    private int ripplePadding;
    private GestureDetector gestureDetector;
    private final Runnable runnable = new Runnable() {
        @Override
        public void run() {
            invalidate();
        }
    };

    private OnRippleCompleteListener onCompletionListener;

    public WaterRelativeLayout(Context context) {
        super(context);
    }

    public WaterRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public WaterRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    /**
     * 初始化些配置信息的方法
     *
     * @param context
     * @param attrs
     */
    private void init(final Context context, final AttributeSet attrs) {
        if (isInEditMode())
            return;

        final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView);
        rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color, getResources().getColor(R.color.water_color));
        rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0);
        hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false);
        isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false);
        rippleDuration = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, rippleDuration);
        frameRate = typedArray.getInteger(R.styleable.RippleView_rv_framerate, frameRate);
        rippleAlpha = typedArray.getInteger(R.styleable.RippleView_rv_alpha, rippleAlpha);
        ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0);
        canvasHandler = new Handler();
        zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f);
        zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200);
        typedArray.recycle();
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(rippleColor);
        paint.setAlpha(rippleAlpha);
        this.setWillNotDraw(false);

        gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public void onLongPress(MotionEvent event) {
                super.onLongPress(event);
                animateRipple(event);
                sendClickEvent(true);
            }

            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                return true;
            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }
        });

        this.setDrawingCacheEnabled(true);
        this.setClickable(true);
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        if (animationRunning) {
            canvas.save();
            if (rippleDuration <= timer * frameRate) {
                animationRunning = false;
                timer = 0;
                durationEmpty = -1;
                timerEmpty = 0;
                if (Build.VERSION.SDK_INT != 23) {
                    canvas.restore();
                }
                invalidate();
                if (onCompletionListener != null)
                    onCompletionListener.onComplete(this);
                return;
            } else
                canvasHandler.postDelayed(runnable, frameRate);

            if (timer == 0)
                canvas.save();
            canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint);
            paint.setColor(Color.parseColor("#ffff4444"));
            if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {
                if (durationEmpty == -1)
                    durationEmpty = rippleDuration - timer * frameRate;

                timerEmpty++;
                final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));
                canvas.drawBitmap(tmpBitmap, 0, 0, paint);
                tmpBitmap.recycle();
            }

            paint.setColor(rippleColor);

            if (rippleType == 1) {
                if ((((float) timer * frameRate) / rippleDuration) > 0.6f)
                    paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));
                else
                    paint.setAlpha(rippleAlpha);
            } else
                paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));

            timer++;
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        WIDTH = w;
        HEIGHT = h;
        scaleAnimation = new ScaleAnimation(1.0f, zoomScale, 1.0f, zoomScale, w / 2, h / 2);
        scaleAnimation.setDuration(zoomDuration);
        scaleAnimation.setRepeatMode(Animation.REVERSE);
        scaleAnimation.setRepeatCount(1);
    }

    /**
     * Launch Ripple animation for the current view with a MotionEvent
     *
     * @param event MotionEvent registered by the Ripple gesture listener
     */
    public void animateRipple(MotionEvent event) {
        createAnimation(event.getX(), event.getY());
    }

    /**
     * 动画效果
     *
     * @param x 动画x轴坐标中心点
     * @param y 动画y轴坐标中心点
     */
    public void animateRipple(final float x, final float y) {
        createAnimation(x, y);
    }

    /**
     * 创建动画效果
     *
     * @param x x轴水平中心坐标点
     * @param y y轴垂直中心坐标点
     */
    private void createAnimation(final float x, final float y) {
        if (this.isEnabled() && !animationRunning) {
            if (hasToZoom)
                this.startAnimation(scaleAnimation);

            radiusMax = Math.max(WIDTH, HEIGHT);

            if (rippleType != 2)
                radiusMax /= 2;

            radiusMax -= ripplePadding;

            if (isCentered || rippleType == 1) {
                this.x = getMeasuredWidth() / 2;
                this.y = getMeasuredHeight() / 2;
            } else {
                this.x = x;
                this.y = y;
            }

            animationRunning = true;

            if (rippleType == 1 && originBitmap == null)
                originBitmap = getDrawingCache(true);

            invalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (gestureDetector.onTouchEvent(event)) {
            animateRipple(event);
            sendClickEvent(false);
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        this.onTouchEvent(event);
        return super.onInterceptTouchEvent(event);
    }

    /**
     * 处理点击事件
     *
     * @param isLongClick 是否是长点击事件
     */
    private void sendClickEvent(final Boolean isLongClick) {
        if (getParent() instanceof AdapterView) {
            final AdapterView adapterView = (AdapterView) getParent();
            final int position = adapterView.getPositionForView(this);
            final long id = adapterView.getItemIdAtPosition(position);
            if (isLongClick) {
                if (adapterView.getOnItemLongClickListener() != null)
                    adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);
            } else {
                if (adapterView.getOnItemClickListener() != null)
                    adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);
            }
        }
    }

    private Bitmap getCircleBitmap(final int radius) {
        final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        final Canvas canvas = new Canvas(output);
        final Paint paint = new Paint();
        final Rect rect = new Rect((int) (x - radius), (int) (y - radius), (int) (x + radius), (int) (y + radius));

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        canvas.drawCircle(x, y, radius, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(originBitmap, rect, rect, paint);

        return output;
    }

    /**
     * 设置水波纹的颜色值
     *
     * @param rippleColor
     */
    @ColorRes
    public void setRippleColor(int rippleColor) {
        this.rippleColor = getResources().getColor(rippleColor);
    }

    public int getRippleColor() {
        return rippleColor;
    }

    public RippleType getRippleType() {
        return RippleType.values()[rippleType];
    }

    /**
     * 设置水波纹类型
     *
     * @param rippleType
     */
    public void setRippleType(final RippleType rippleType) {
        this.rippleType = rippleType.ordinal();
    }

    public Boolean isCentered() {
        return isCentered;
    }

    /**
     * 设置中心播放动画view
     *
     * @param isCentered
     */
    public void setCentered(final Boolean isCentered) {
        this.isCentered = isCentered;
    }

    public int getRipplePadding() {
        return ripplePadding;
    }

    /**
     * 设置填充
     *
     * @param ripplePadding
     */
    public void setRipplePadding(int ripplePadding) {
        this.ripplePadding = ripplePadding;
    }

    public Boolean isZooming() {
        return hasToZoom;
    }

    /**
     * 设置焦点
     *
     * @param hasToZoom 默认false
     */
    public void setZooming(Boolean hasToZoom) {
        this.hasToZoom = hasToZoom;
    }

    public float getZoomScale() {
        return zoomScale;
    }

    /**
     * 设置缩放
     *
     * @param zoomScale 默认1.0f
     */
    public void setZoomScale(float zoomScale) {
        this.zoomScale = zoomScale;
    }

    public int getZoomDuration() {
        return zoomDuration;
    }

    /**
     * 设置持续时长
     *
     * @param zoomDuration 默认 200ms
     */
    public void setZoomDuration(int zoomDuration) {
        this.zoomDuration = zoomDuration;
    }

    public int getRippleDuration() {
        return rippleDuration;
    }

    /**
     * 设置动画持续时长
     *
     * @param rippleDuration 默认 400ms
     */
    public void setRippleDuration(int rippleDuration) {
        this.rippleDuration = rippleDuration;
    }

    public int getFrameRate() {
        return frameRate;
    }

    /**
     * 设置水波纹帧
     *
     * @param frameRate 默认 10
     */
    public void setFrameRate(int frameRate) {
        this.frameRate = frameRate;
    }

    public int getRippleAlpha() {
        return rippleAlpha;
    }

    /**
     * 设置水波纹颜色透明值
     *
     * @param rippleAlpha 取值 0 ~ 255, 默认 90
     */
    public void setRippleAlpha(int rippleAlpha) {
        this.rippleAlpha = rippleAlpha;
    }

    public void setOnRippleCompleteListener(OnRippleCompleteListener listener) {
        this.onCompletionListener = listener;
    }

    /**
     * 水波纹结束动画的回调方法
     */
    public interface OnRippleCompleteListener {
        void onComplete(WaterRelativeLayout rippleView);
    }

    /**
     * 枚举类型
     */
    public enum RippleType {
        SIMPLE(0),
        DOUBLE(1),
        RECTANGLE(2);

        int type;

        RippleType(int type) {
            this.type = type;
        }
    }
}


8.圆环头像图片实现

效果图:



代码实现:

/*
 * Copyright 2014 - 2015 Henning Dodenhof
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.example.lainanzhou.customviewdemo.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.util.AttributeSet;
import android.widget.ImageView;

import com.example.lainanzhou.customviewdemo.R;

/**
 * TODO:
 * 实现圆环头像的imageView
 *
 * @author Joker
 */

public class CircleImageView extends ImageView {

    private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;

    private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
    private static final int COLORDRAWABLE_DIMENSION = 2;

    private static final int DEFAULT_BORDER_WIDTH = 0;
    private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
    private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
    private static final boolean DEFAULT_BORDER_OVERLAY = false;

    private final RectF mDrawableRect = new RectF();
    private final RectF mBorderRect = new RectF();

    private final Matrix mShaderMatrix = new Matrix();
    private final Paint mBitmapPaint = new Paint();
    private final Paint mBorderPaint = new Paint();
    private final Paint mFillPaint = new Paint();

    private int mBorderColor = DEFAULT_BORDER_COLOR;
    private int mBorderWidth = DEFAULT_BORDER_WIDTH;
    private int mFillColor = DEFAULT_FILL_COLOR;

    private Bitmap mBitmap;
    private BitmapShader mBitmapShader;
    private int mBitmapWidth;
    private int mBitmapHeight;

    private float mDrawableRadius;
    private float mBorderRadius;

    private ColorFilter mColorFilter;

    private boolean mReady;
    private boolean mSetupPending;
    private boolean mBorderOverlay;

    public CircleImageView(Context context) {
        super(context);

        init();
    }

    public CircleImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);

        mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
        mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
        mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
        mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);

        a.recycle();

        init();
    }

    private void init() {
        super.setScaleType(SCALE_TYPE);
        mReady = true;

        if (mSetupPending) {
            setup();
            mSetupPending = false;
        }
    }

    @Override
    public ScaleType getScaleType() {
        return SCALE_TYPE;
    }

    @Override
    public void setScaleType(ScaleType scaleType) {
        if (scaleType != SCALE_TYPE) {
            throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
        }
    }

    @Override
    public void setAdjustViewBounds(boolean adjustViewBounds) {
        if (adjustViewBounds) {
            throw new IllegalArgumentException("adjustViewBounds not supported.");
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mBitmap == null) {
            return;
        }

        if (mFillColor != Color.TRANSPARENT) {
            canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mFillPaint);
        }
        canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mBitmapPaint);
        if (mBorderWidth != 0) {
            canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mBorderRadius, mBorderPaint);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        setup();
    }

    public int getBorderColor() {
        return mBorderColor;
    }

    public void setBorderColor(@ColorInt int borderColor) {
        if (borderColor == mBorderColor) {
            return;
        }

        mBorderColor = borderColor;
        mBorderPaint.setColor(mBorderColor);
        invalidate();
    }

    public void setBorderColorResource(@ColorRes int borderColorRes) {
        setBorderColor(getContext().getResources().getColor(borderColorRes));
    }

    public int getFillColor() {
        return mFillColor;
    }

    public void setFillColor(@ColorInt int fillColor) {
        if (fillColor == mFillColor) {
            return;
        }

        mFillColor = fillColor;
        mFillPaint.setColor(fillColor);
        invalidate();
    }

    public void setFillColorResource(@ColorRes int fillColorRes) {
        setFillColor(getContext().getResources().getColor(fillColorRes));
    }

    public int getBorderWidth() {
        return mBorderWidth;
    }

    public void setBorderWidth(int borderWidth) {
        if (borderWidth == mBorderWidth) {
            return;
        }

        mBorderWidth = borderWidth;
        setup();
    }

    public boolean isBorderOverlay() {
        return mBorderOverlay;
    }

    public void setBorderOverlay(boolean borderOverlay) {
        if (borderOverlay == mBorderOverlay) {
            return;
        }

        mBorderOverlay = borderOverlay;
        setup();
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        mBitmap = bm;
        setup();
    }

    @Override
    public void setImageDrawable(Drawable drawable) {
        super.setImageDrawable(drawable);
        mBitmap = getBitmapFromDrawable(drawable);
        setup();
    }

    @Override
    public void setImageResource(@DrawableRes int resId) {
        super.setImageResource(resId);
        mBitmap = getBitmapFromDrawable(getDrawable());
        setup();
    }

    @Override
    public void setImageURI(Uri uri) {
        super.setImageURI(uri);
        mBitmap = uri != null ? getBitmapFromDrawable(getDrawable()) : null;
        setup();
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        if (cf == mColorFilter) {
            return;
        }

        mColorFilter = cf;
        mBitmapPaint.setColorFilter(mColorFilter);
        invalidate();
    }

    private Bitmap getBitmapFromDrawable(Drawable drawable) {
        if (drawable == null) {
            return null;
        }

        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        try {
            Bitmap bitmap;

            if (drawable instanceof ColorDrawable) {
                bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
            } else {
                bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
            }

            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
            return bitmap;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private void setup() {
        if (!mReady) {
            mSetupPending = true;
            return;
        }

        if (getWidth() == 0 && getHeight() == 0) {
            return;
        }

        if (mBitmap == null) {
            invalidate();
            return;
        }

        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        mBitmapPaint.setAntiAlias(true);
        mBitmapPaint.setShader(mBitmapShader);

        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setColor(mBorderColor);
        mBorderPaint.setStrokeWidth(mBorderWidth);

        mFillPaint.setStyle(Paint.Style.FILL);
        mFillPaint.setAntiAlias(true);
        mFillPaint.setColor(mFillColor);

        mBitmapHeight = mBitmap.getHeight();
        mBitmapWidth = mBitmap.getWidth();

        mBorderRect.set(0, 0, getWidth(), getHeight());
        mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);

        mDrawableRect.set(mBorderRect);
        if (!mBorderOverlay) {
            mDrawableRect.inset(mBorderWidth, mBorderWidth);
        }
        mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);

        updateShaderMatrix();
        invalidate();
    }

    private void updateShaderMatrix() {
        float scale;
        float dx = 0;
        float dy = 0;

        mShaderMatrix.set(null);

        if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
            scale = mDrawableRect.height() / (float) mBitmapHeight;
            dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
        } else {
            scale = mDrawableRect.width() / (float) mBitmapWidth;
            dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
        }

        mShaderMatrix.setScale(scale, scale);
        mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);

        mBitmapShader.setLocalMatrix(mShaderMatrix);
    }

}


最后附带项目下载链接地址:点击打开项目代码demo链接

后期若开发遇到常用的功能将会继续添加,若项目中出现有问题也欢迎大神指点一二。


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值