android自定义view-强大的点击灰色背景imageview


前言:

微信作为很火的社交平台广受大众推崇,作为开发者不仅要学会使用软件,更重要的是要琢磨那些吸引人的控件的研发思路。刷了好久的朋友圈,作为一个安卓开发者来说,朋友圈九宫格的图片显示,以及点击图片会有灰色蒙板的效果吸引了我,为了方便大家,也为了给自己温习知识,写下这篇博文。

效果图:

就是这样的效果!

正文:

今天我们就来实现这样的一个效果,在我们动笔写代码之前,我们必须要理清自己的思路,不能一开始就盲目的去写代码,磨刀不误砍柴工。我们唯一能够确定的是,继承自imageview。然后呢?然后重写构造方法,由于用到了点击,所以还要去手动处理一下onTouchEvent方法。看似困难的问题处理起来也会游刃有余了。想法都是对的,其实我们还少分析一个问题,图片会分为本地和远程两种形式,这样的图片如何去加一个灰色的蒙板,远程图片又如何加载呢?带着这些个问题,我们来一一探寻答案。

思考一:继承ImageView

public class GrayScaleImageView extends ImageView 

思考二:重写构造方法

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

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

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

我没有进行一些初始化操作,因为这里还没有什么需要初始化。

思考三:处理onTouchEvent事件

@Override
	public boolean onTouchEvent(MotionEvent event) {
		Drawable drawable = getDrawable();
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (drawable != null) {
				// drawable.mutate使得此drawable共享状态,改变时全部改变
				drawable.mutate().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
			}
			break;
		case MotionEvent.ACTION_MOVE:
			break;
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			if (drawable != null) {
				drawable.mutate().clearColorFilter();
			}
			break;
		}
		return true;
		// return super.onTouchEvent(event);
	}

这是整个博文最核心的一部分。首先我们通过getDrawable()获取ImageView的drawable图片,手指按下的时候需要给这个drawable加上灰色蒙板,我们通过:

drawable.mutate().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);

这段代码给drawable加上了一个颜色过滤,那么这边的mutate方法,需要看一下官方解释:

最重要的一句话,By default, all drawables instances loaded from the same resource share a common state; if you modify the state of one instance, all the other instances will receive the same modification.中文简言就是状态共享。

手指抬起的时候,就要清除我们加的过滤层。

drawable.mutate().clearColorFilter();

这里我需要特别提醒的就是,这边的事件处理完之后,需要return true,如果你return super.onTouchEvent(event);就会发现move,和up事件不会执行,只会执行down事件,觉得奇怪的同学去深入看一下安卓的事件分发处理机制。

思考四:如何设置imageview本地和远程图片

首先,我们需要确定一下我们的远程图片加载框架,网上有很多优秀的图片加载框架,在前面的博文中我也有提到过,这里我用的是Picasso。那么在什么时候去设置图片呢?

android view有以下14个周期:
1、onFinishInflate() 当View中所有的子控件均被映射成xml后触发 。
2、onMeasure( int , int ) 确定所有子元素的大小 。
3、onLayout( boolean , int , int , int , int ) 当View分配所有的子元素的大小和位置时触发 。
4、onSizeChanged( int , int , int , int ) 当view的大小发生变化时触发 。
5、onDraw(Canvas) view渲染内容的细节。 
6、onKeyDown( int , KeyEvent) 有按键按下后触发 。
7、onKeyUp( int , KeyEvent) 有按键按下后弹起时触发 。
8、onTrackballEvent(MotionEvent) 轨迹球事件 。 
9、onTouchEvent(MotionEvent) 触屏事件 。
10、onFocusChanged( boolean , int , Rect) 当View获取或失去焦点时触发 。
11、onWindowFocusChanged( boolean ) 当窗口包含的view获取或失去焦点时触发 。
12、onAttachedToWindow() 当view被附着到一个窗口时触发 。
13、onDetachedFromWindow() 当view离开附着的窗口时触发,Android123提示该方法和 onAttachedToWindow() 是相反的。 
14、onWindowVisibilityChanged( int ) 当窗口中包含的可见的view发生变化时触发。

安卓的大致执行流程为:从构造方法开始,依次执行onAttachToWindow——>onMeasure——>onSizeChanged——>onLayout——>onMeasure——>onLayout——>onDraw——>onDetachedFromWindow

看到这边的话,就应该确定了,我们在onAttachToWindow方法中进行图片设置,在onDetachedFromWindow取消图片设置

	@Override
	protected void onAttachedToWindow() {
		isAttachWindow = true;
		if (!TextUtils.isEmpty(url)) {
			if (isAttachWindow) {
				Picasso.with(getContext()).load(url).placeholder(new ColorDrawable(Color.parseColor("#f5f5f5"))).into(this);
			}
		}
		super.onAttachedToWindow();
	}

	@Override
	protected void onDetachedFromWindow() {
		Picasso.with(getContext()).cancelRequest(this);
		isAttachWindow = false;
		setImageBitmap(null);
		super.onDetachedFromWindow();
	}

如果是本地图片的话,我们直接在xml中设置android:src,如果是远程的图片的话,就需要在activity中调用这个方法。

public void setImageUrl(String url) {
		if (!TextUtils.isEmpty(url)) {
			this.url = url;
		}
	}

好了,我贴上全部代码咯,GrayScaleImageView.java:

package com.beyole.view;

import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ImageView;

import com.squareup.picasso.Picasso;

public class GrayScaleImageView extends ImageView {

	// 图片地址
	private String url;
	// 是否依附在window上
	private boolean isAttachWindow = false;

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

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

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

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		Drawable drawable = getDrawable();
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (drawable != null) {
				// drawable.mutate使得此drawable共享状态,改变时全部改变
				drawable.mutate().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
			}
			break;
		case MotionEvent.ACTION_MOVE:
			break;
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			if (drawable != null) {
				drawable.mutate().clearColorFilter();
			}
			break;
		}
		return true;
		// return super.onTouchEvent(event);
	}

	@Override
	protected void onAttachedToWindow() {
		isAttachWindow = true;
		if (!TextUtils.isEmpty(url)) {
			if (isAttachWindow) {
				Picasso.with(getContext()).load(url).placeholder(new ColorDrawable(Color.parseColor("#f5f5f5"))).into(this);
			}
		}
		super.onAttachedToWindow();
	}

	@Override
	protected void onDetachedFromWindow() {
		Picasso.with(getContext()).cancelRequest(this);
		isAttachWindow = false;
		setImageBitmap(null);
		super.onDetachedFromWindow();
	}

	public void setImageUrl(String url) {
		if (!TextUtils.isEmpty(url)) {
			this.url = url;
		}
	}
}

使用:MainActivity.java

package com.beyole.grayscaleimageview;

import android.app.Activity;
import android.os.Bundle;

import com.beyole.view.GrayScaleImageView;

public class MainActivity extends Activity {

	private GrayScaleImageView imageView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//imageView = (GrayScaleImageView) findViewById(R.id.img1);
		//imageView.setImageUrl("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1461129468&di=07dab3112befff0b84bd7c5933ff5523&src=http://img5.duitang.com/uploads/item/201411/08/20141108162654_xthYT.jpeg");
	}

}

activity_main.xml:



    

行了,其实写这篇博客是为下篇微信朋友圈九宫格显示图片做准备的,就当是下篇博文的预告了吧。

后语:

好了,今天的代码教程到此结束。赶着去改论文了,忙里偷着写这一篇博客,我真的不适合搞论文写作。。抓狂

csdn下载地址:http://download.csdn.net/detail/smarticeberg/9496918
github地址:
https://github.com/xuejiawei/beyole_grayscaleimageview 欢迎fork or star

题外话:

Android交流群:279031247(广告勿入)

新浪微博:SmartIceberg