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

来源:互联网 发布:淘宝皮草店装修效果图 编辑:程序博客网 时间:2024/05/16 09:33

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 0