Android自定义系列(1)——自定义view 仿微信viewPage底部显示图标(整理来自慕课网)

这里我整理了几个大神的技术博文,结合着来自慕课网的代码,梳理一下自定义view要做的几个步骤。

1.在value中建立一个attr.xml,再声明几个属性:


    
    

    
    

    
     
     
    
     
     
    
     
     
    
     
     

    
     
     
        
      
      
        
      
      
        
      
      
        
      
      
    
     
     


    
    

2.在构造函数中初始化view中的属性数据:

	/**
	 * 构造并初始化view的属性数据
	 * 
	 * @param context
	 * @param attrs
	 * @param defStyleAttr
	 */
	public ChangeColorIconWithText(Context context, AttributeSet attrs,
			int defStyleAttr)
	{
		super(context, attrs, defStyleAttr);

		
		TypedArray a = context.obtainStyledAttributes(attrs,
				R.styleable.ChangeColorIconWithText);
		
		//获得a中的view属性的个数
		int n = a.getIndexCount();

		for (int i = 0; i < n; i++)
		{
			//通过下标获得view属性的id
			int attr = a.getIndex(i);
			switch (attr)
			{
			//获得子view的数据,并赋值到全局变量
			case R.styleable.ChangeColorIconWithText_icon:
				BitmapDrawable drawable = (BitmapDrawable) a.getDrawable(attr);
				mIconBitmap = drawable.getBitmap();
				break;
			case R.styleable.ChangeColorIconWithText_color:
				mColor = a.getColor(attr, 0xFF45C01A);
				break;
			case R.styleable.ChangeColorIconWithText_text:
				mText = a.getString(attr);
				break;
			case R.styleable.ChangeColorIconWithText_text_size:
				mTextSize = (int) a.getDimension(attr, TypedValue
						.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12,
								getResources().getDisplayMetrics()));
				break;
			}

		}

		//回收
		a.recycle();

		//初始化控件
		mTextBound = new Rect();
		mTextPaint = new Paint();
		mTextPaint.setTextSize(mTextSize);
		mTextPaint.setColor(0Xff555555);
		//获得文本绘制区域
		mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBound);

	}

3.重写onMeasure()方法,这个方法是用来确定布局大小及位置的

	//绘制布局的大小,必须实现的方法
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int iconWidth = Math.min(getMeasuredWidth() - getPaddingLeft()
				- getPaddingRight(), getMeasuredHeight() - getPaddingTop()
				- getPaddingBottom() - mTextBound.height());

		int left = getMeasuredWidth() / 2 - iconWidth / 2;
		int top = getMeasuredHeight() / 2 - (mTextBound.height() + iconWidth)
				/ 2;
		//确定icon的区域
		mIconRect = new Rect(left, top, left + iconWidth, top + iconWidth);
	}

4.重写onDraw()方法,这个方法是用来绘制显示界面的

@Override
	protected void onDraw(Canvas canvas)
	{
		//绘制大小为mIcomRect的图片数据mIcomBitmap
		canvas.drawBitmap(mIconBitmap, null, mIconRect, null);

		int alpha = (int) Math.ceil(155 * mAlpha);

		// 设置icon的颜色
		setupTargetBitmap(alpha);
		// 设置字体
		drawSourceText(canvas, alpha);
		drawTargetText(canvas, alpha);
		
		canvas.drawBitmap(mBitmap, 0, 0, null);

	}

	/**
	 * 绘制字体
	 * 
	 * @param canvas
	 * @param alpha
	 */
	private void drawTargetText(Canvas canvas, int alpha)
	{
		mTextPaint.setColor(mColor);
		mTextPaint.setAlpha(alpha);
		int x = getMeasuredWidth() / 2 - mTextBound.width() / 2;
		int y = mIconRect.bottom + mTextBound.height();
		//绘制文本,mTextPaint为文本绘制区域
		canvas.drawText(mText, x, y, mTextPaint);

	}

	/**
	 * 绘制颜色变换的字体
	 * 
	 * @param canvas
	 * @param alpha
	 */
	private void drawSourceText(Canvas canvas, int alpha)
	{
		mTextPaint.setColor(0xff333333);
		mTextPaint.setAlpha(155 - alpha);
		int x = getMeasuredWidth() / 2 - mTextBound.width() / 2;
		int y = mIconRect.bottom + mTextBound.height();
		canvas.drawText(mText, x, y, mTextPaint);

	}

	/**
	 * 绘制颜色可以变换的icon
	 */
	private void setupTargetBitmap(int alpha)
	{
		mBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
				Config.ARGB_8888);
		mCanvas = new Canvas(mBitmap);
		mPaint = new Paint();
		mPaint.setColor(mColor);
		//用来防止边缘的锯齿
		mPaint.setAntiAlias(true);
		//防抖动
		mPaint.setDither(true);
		mPaint.setAlpha(alpha);
		mCanvas.drawRect(mIconRect, mPaint);
		//设置图像的混合模式
		mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
		mPaint.setAlpha(155);
		mCanvas.drawBitmap(mIconBitmap, null, mIconRect, mPaint);
	}

5.当view的数据改变时,需要重绘,调用android重绘的方法

	/**
	 * 设置icon透明度
	 */
	public void setIconAlpha(float alpha)
	{
		this.mAlpha = alpha;
		invalidateView();
	}

	/**
	 * 重绘
	 */
	private void invalidateView()
	{
		//判断是否在UI主线程
		if (Looper.getMainLooper() == Looper.myLooper())
		{
			invalidate();//重绘view
		} else
		{
			postInvalidate();//提交重绘view
		}
	}

6.再MainActivity中使用自定义的view

6.1在MainActivity中初始化

	private void initView()
	{
		mViewPager = (ViewPager) findViewById(R.id.id_viewpager);

		ChangeColorIconWithText one = (ChangeColorIconWithText) findViewById(R.id.id_indicator_one);
		mTabIndicators.add(one);
		ChangeColorIconWithText two = (ChangeColorIconWithText) findViewById(R.id.id_indicator_two);
		mTabIndicators.add(two);
		ChangeColorIconWithText three = (ChangeColorIconWithText) findViewById(R.id.id_indicator_three);
		mTabIndicators.add(three);
		ChangeColorIconWithText four = (ChangeColorIconWithText) findViewById(R.id.id_indicator_four);
		mTabIndicators.add(four);

		one.setOnClickListener(this);
		two.setOnClickListener(this);
		three.setOnClickListener(this);
		four.setOnClickListener(this);

		one.setIconAlpha(1.0f);

	}

6.2设置点击触发,改变颜色

	private void clickTab(View v)
	{
		resetOtherTabs();

		switch (v.getId())
		{
		case R.id.id_indicator_one:
			mTabIndicators.get(0).setIconAlpha(1.0f);
			mViewPager.setCurrentItem(0, false);
			break;
		case R.id.id_indicator_two:
			mTabIndicators.get(1).setIconAlpha(1.0f);
			mViewPager.setCurrentItem(1, false);
			break;
		case R.id.id_indicator_three:
			mTabIndicators.get(2).setIconAlpha(1.0f);
			mViewPager.setCurrentItem(2, false);
			break;
		case R.id.id_indicator_four:
			mTabIndicators.get(3).setIconAlpha(1.0f);
			mViewPager.setCurrentItem(3, false);
			break;
		}
	}

6.3设置viewPage滑动触发,逐渐改变颜色

	@Override
	public void onPageScrolled(int position, float positionOffset,
			int positionOffsetPixels)
	{
		// Log.e("TAG", "position = " + position + " ,positionOffset =  "
		// + positionOffset);
		if (positionOffset > 0)
		{
			ChangeColorIconWithText left = mTabIndicators.get(position);
			ChangeColorIconWithText right = mTabIndicators.get(position + 1);
			left.setIconAlpha(1 - positionOffset);
			right.setIconAlpha(positionOffset);
		}

	}

这里再详细讲一下关于绘制可以通过透明度改变颜色的那个方法。

	/**
	 * 绘制颜色可以变换的icon
	 */
	private void setupTargetBitmap(int alpha)
	{
		mBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
				Config.ARGB_8888);
		mCanvas = new Canvas(mBitmap);
		mPaint = new Paint();
		mPaint.setColor(mColor);
		//用来防止边缘的锯齿
		mPaint.setAntiAlias(true);
		//防抖动
		mPaint.setDither(true);
		mPaint.setAlpha(alpha);
		mCanvas.drawRect(mIconRect, mPaint);
		//设置图像的混合模式
		mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
		mPaint.setAlpha(155);
		mCanvas.drawBitmap(mIconBitmap, null, mIconRect, mPaint);
	}

第一点:是关于Paint.setAntiAlias()防止边缘锯齿

在Android中,目前,我知道有两种出现锯齿的情况。
 ① 当我们用Canvas绘制位图的时候,如果对位图进行了选择,则位图会出现锯齿。
 ② 在用View的RotateAnimation做动画时候,如果View当中包含有大量的图形,也会出现锯齿。我们分别以这两种情况加以考虑。 
◆ 用Canvas绘制位的的情况。在用Canvas绘制位图时,一般地,我们使用drawBitmap函数家族,在这些函数中,都有一个Paint参数,要做到防止锯齿,我们就要使用到这个参数。如下:首先在你的构造函数中,需要创建一个Paint。 Paint mPaint = new Paint(); 然后,您需要设置两个参数: 1)mPaint.setAntiAlias(); 2)mPaint.setBitmapFilter(true)。第一个函数是用来防止边缘的锯齿,第二个函数是用来对位图进行滤波处理。最后,在画图的时候,调用drawBitmap函数,只需要将整个Paint传入即可。 
◆ 有时候,当你做RotateAnimation时,你会发现,讨厌的锯齿又出现了。这个时候,由于你不能控制位图的绘制,只能用其他方法来实现防止锯齿。另外,如果你画的位图很多。不想每个位图的绘制都传入一个Paint。还有的时候,你不可能控制每个窗口的绘制的时候,您就需要用下面的方法来处理——对整个Canvas进行处理。 1)在您的构造函数中,创建一个Paint滤波器。 PaintFlagsDrawFilter mSetfil = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG);第一个参数是你要清除的标志位,第二个参数是你要设置的标志位。此处设置为对位图进行滤波。 2)当你在画图的时候,如果是View则在onDraw当中,如果是ViewGroup则在dispatchDraw中调用如下函数。 canvas.setDrawFilter( mSetfil ); 
★ 最后,另外,在Drawable类及其子类中,也有函数setFilterBitmap可以用来对Bitmap进行滤波处理,这样,当你选择Drawable时,会有抗锯齿的效果。(以上是来自转载博文。)

第二点:Paint的常用方法

void  setARGB(int a, int r, int g, int b)  设置Paint对象颜色,参数一为alpha透明通道

void  setAlpha(int a)  设置alpha不透明度,范围为0~255

void  setAntiAlias(boolean aa)  //是否抗锯齿

void  setColor(int color)  //设置颜色,这里Android内部定义的有Color类包含了一些常见颜色定义
 . 
void  setFakeBoldText(boolean fakeBoldText)  //设置伪粗体文本
  
void  setLinearText(boolean linearText)  //设置线性文本
 
PathEffect  setPathEffect(PathEffect effect)  //设置路径效果
 
Rasterizer  setRasterizer(Rasterizer rasterizer) //设置光栅化
 
Shader  setShader(Shader shader)  //设置阴影 

void  setTextAlign(Paint.Align align)  //设置文本对齐

void  setTextScaleX(float scaleX)  //设置文本缩放倍数,1.0f为原始
  
void  setTextSize(float textSize)  //设置字体大小
 
Typeface  setTypeface(Typeface typeface)  //设置字体,Typeface包含了字体的类型,粗细,还有倾斜、颜色等。

void  setUnderlineText(boolean underlineText)  //设置下划线


Paint paint = new Paint();
 
            paint.setAntiAlias(true);                 //防锯齿
             paint.setDither(true);                       //防抖动
             paint.setStyle(Paint.Style.STROKE);                   //画笔类型 STROKE空心 FILL 实心

第三点:paint.setXfermode() 的具体用法
mPaint =  new  Paint();
mPaint.setXfermode( new  PorterDuffXfermode(PorterDuff.Mode.SCREEN));

Canvas canvas =  new  Canvas(Src);

paint.setXfermode( new  PorterDuffXfermode(Mode.SRC_IN));

canvas.drawBitmap(Dst, 0f, 0f, paint);

这个是模式的示例图片:








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值