最近看了微信客户端主页面的效果挺不过的,就想模仿搞一个,具体实现步骤如下:
一、新建一个attrs.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 自定义属性 -->
<attr name="icon_color_change_tab" format="reference"></attr>
<attr name="color_change_tab" format="color"></attr>
<attr name="text_change_tab" format="string"></attr>
<attr name="text_size_change_tab" format="dimension"></attr>
<declare-styleable name="ColorChangeTab">
<attr name="icon_color_change_tab"></attr>
<attr name="color_change_tab"></attr>
<attr name="text_change_tab"></attr>
<attr name="text_size_change_tab"></attr>
</declare-styleable>
</resources>
二、再新建一个java文件,引用上面的自定义控件属性
package com.swun.jkt.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Looper;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import com.swun.jkt.R;
/**
* 自定义控件需要的步骤如下: 1、自定义属性attr.xml 2、在布局文件中使用自定义属性(命名空间) 3、在自定义控件的构造方法中获取自定义属性
* 4、onMeasure 5、onDraw
*
* @author littleRich
*
*/
public class ColorChangeTab extends View {
private int mColor = 0xff45c01A;// 图标背景色,绿色
public static final int TEXT_COLOR = 0xff333333;// 文本默认的颜色(灰色)
private Bitmap mIconBitmap;// 图标
private String mText = "";// 图标下的文字
// 设置默认文字大小为12sp
private int mTextSize = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics());
private Canvas mCanvas;// 给图标绘制背景色的画布
private Bitmap mBitmap;// 图标下的背景色(用于动态修改颜色,出现切换标签的渐变效果)
private Paint mPaint;
private float alpha;// 透明度(用户动态修改颜色,出现渐变效果),范围为:0~1
private Rect mIconRect;
private Rect mTextBound;// 包裹文字的矩形区域
private Paint mTextPaint;// 画文字的画笔
public ColorChangeTab(Context context) {
// 调用两个参数的构造方法
this(context, null);
}
public ColorChangeTab(Context context, AttributeSet attrs) {
// 调用三个参数的构造方法
this(context, attrs, 0);
}
public ColorChangeTab(Context context, AttributeSet attrs, int defStyleAttr) {
// 三个参数的构造方法
super(context, attrs, defStyleAttr);
// 获取所有自定义属性的值
TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
R.styleable.ColorChangeTab);
// 获取自定属性的个数
int count = mTypedArray.getIndexCount();
/**
* 获取所有自定义属性
*/
for (int i = 0; i < count; i++) {
// 获取索引ID
int ID = mTypedArray.getIndex(i);
switch (ID) {
case R.styleable.ColorChangeTab_icon_color_change_tab:// 如果是图标
BitmapDrawable mBitmapDrawable = (BitmapDrawable) mTypedArray
.getDrawable(ID);
mIconBitmap = mBitmapDrawable.getBitmap();
break;
case R.styleable.ColorChangeTab_color_change_tab:// 如果是颜色
mColor = mTypedArray.getColor(ID, 0xff45c01A);
break;
case R.styleable.ColorChangeTab_text_change_tab:// 如果是文本内容
mText = mTypedArray.getString(ID);
break;
case R.styleable.ColorChangeTab_text_size_change_tab:// 如果是文本尺寸
mTextSize = (int) mTypedArray.getDimension(ID, TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12,
getResources().getDisplayMetrics()));
break;
default:
break;
}
}
// 回收TypedArray
mTypedArray.recycle();
mTextBound = new Rect();// 新建文本矩形区域
mTextPaint = new Paint();// 新建画笔
mTextPaint.setTextSize(mTextSize);// 文字大小
mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBound);// 测量取得文本区域
// 宽度,高度
}
/**
* 测量大小
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 获取icon的边长尺寸
*
* 说明:icon是正方形,具体取得其边长的算法如下
*
* 边长=水平方向剩余空间&垂直方向剩余空间的 最小值
*
* 其中,水平方向剩余空间:控件所有长度-左右的padding 垂直方向剩余空间:控件所有高度-上下的padding-文字矩形区域 的高度
*/
int iconWidth = Math.min(getMeasuredWidth() - getPaddingLeft()
- getPaddingRight(), getMeasuredHeight() - getPaddingTop()
- getPaddingBottom() - mTextBound.height());
// 左上角的横坐标
int left = (getMeasuredWidth() - iconWidth) / 2;
// 左上角的纵坐标
int right = (getMeasuredHeight() - iconWidth - mTextBound.height()) / 2;
// icon的矩形区域
mIconRect = new Rect(left, right, left + iconWidth, right + iconWidth);
}
@Override
protected void onDraw(Canvas canvas) {
/**
* drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint); <br/>
* bitmap:具体要绘制的图片 src: 是对图片进行裁截,若是空null则显示整个图片 <br/>
* dst:是图片在Canvas画布中显示的区域,<br/>
* 大于src则把src的裁截区放大,<br/>
* 小于src则把src的裁截区缩小。<br/>
* 如果绘制的是bitmap,paint设置为空
*/
canvas.drawBitmap(mIconBitmap, null, mIconRect, null);
// 颜色透明度值;this.alpha为0~1的float值。alpha为0~255的透明度值
int alpha = (int) Math.ceil(255 * this.alpha);
// 内存中准备mBitmap:setAlpha,纯色,xfermode,图标
setupTargetBitmap(alpha);
// 1、绘制源文本 2、绘制变色的文本
// 绘制源文本
drawSourceText(canvas, alpha);
// 绘制变色的文本
drawTargetText(canvas, alpha);
canvas.drawBitmap(mBitmap, 1/2, 1/2, null);
}
/**
* 设置可变色的文本
* @param canvas 画布
* @param alpha 绿色的透明度
*/
private void drawTargetText(Canvas canvas, int alpha) {
// 设置绿色,与透明度,以便渐变。
mTextPaint.setColor(mColor);
mTextPaint.setAlpha(alpha);
// 显示文字的左上角的横坐标(在icon下面,并且居中)
int left = getMeasuredWidth() / 2 - mIconRect.width() / 2;
// 显示文字的左下角的纵坐标
int right = mIconRect.bottom + mTextBound.height();
// 绘制文本
canvas.drawText(mText, left, right, mTextPaint);
}
/**
* 绘制源文本。
*
* @param canvas
* 画布(onDraw()传入)
* @param alpha
* 透明度
*/
private void drawSourceText(Canvas canvas, int alpha) {
mTextPaint.setAlpha(255 - alpha);//文字透明度
mTextPaint.setColor(TEXT_COLOR);// 文字颜色(灰色)
// 显示文字的左上角的横坐标(在icon下面,并且居中)
int left = getMeasuredWidth() / 2 - mIconRect.width() / 2;
// 显示文字的左下角的纵坐标
int right = mIconRect.bottom + mTextBound.height();
// 绘制文本
canvas.drawText(mText, left, right, mTextPaint);
}
/**
* 在内存中绘制可变色的Icon
*
* @param alpha
* 透明度值(用户动态改变图标,渐变颜色)
*/
private void setupTargetBitmap(int alpha) {
/**
* int width -->宽 <br/>
* int height -->高 <br/>
* Config config --> android.graphics.Bitmap.Config是一个枚举类型,里面定义了位图的四种格式.
* ALPHA_8:数字为8,图形参数应该由一个字节来表示,应该是一种8位的位图
* ARGB_4444:4+4+4+4=16,图形的参数应该由两个字节来表示,应该是一种16位的位图.
* ARGB_8888:8+8+8+8=32,图形的参数应该由四个字节来表示,应该是一种32位的位图.
* RGB_565:5+6+5=16,图形的参数应该由两个字节来表示,应该是一种16位的位图.
*/
mBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
Config.ARGB_8888);
// 创建一个纯色的画布
mCanvas = new Canvas(mBitmap);
// 创建一支画笔
mPaint = new Paint();
mPaint.setColor(mColor);
// 去锯齿
mPaint.setAntiAlias(true);
/**
* Dither(图像的抖动处理,当每个颜色值以低于8位表示时,对应图像做抖动处理可以实现在可显示颜色总数比较低(比如256色)
* 时还保持较好的显示效果
*/
mPaint.setDither(true);
mPaint.setAlpha(alpha);
// 在图标范围绘制纯色
mCanvas.drawRect(mIconRect, mPaint);
// 设置Xfermode
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
// 将画笔设为不透明
mPaint.setAlpha(255);
mCanvas.drawBitmap(mIconBitmap, null, mIconRect, mPaint);
}
/**
* 设置图标的透明度
* @param alpha 绿色的透明度值。0~1,为0代表全透明。1代表不透明
*/
public void setIconAlpha(float alpha){
this.alpha = alpha;
//更新图标
invalidateView();
}
/**
* 重绘我们的控件
* 注意。我们需要判断是否是主线程。如果是主线程,直接调用invalidate()
* 非主线程,调用postInvalidate();
*/
private void invalidateView() {
//如果这是主线程提出更新
if(Looper.getMainLooper() == Looper.myLooper()){
invalidate();
}
//如果不是主线程
else{
postInvalidate();
}
}
//当控件暂时被系统回收后,保存数据
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putFloat("alpha", alpha);
bundle.putParcelable("parentData", super.onSaveInstanceState());
return bundle;
}
//当控件恢复时,恢复暂存数据
@Override
protected void onRestoreInstanceState(Parcelable state) {
if(state instanceof Bundle){
alpha = ((Bundle) state).getFloat("alpha");
super.onRestoreInstanceState(((Bundle) state).getParcelable("parentData"));
}
return ;
}
}
0、写一个fragment文件
/**
*
*/package com.swun.jkt.fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
/**
* @author: littleRich
* @date: 2015-8-9 上午11:20:27
* @version 1.0
* @parameter
* @since
* @return
*/
public class IMFragment extends Fragment {
/* (non-Javadoc)
* @see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
*/
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
TextView tv = new TextView(getActivity());
tv.setText("world");
return tv;
}
}
1、接着使用上面的fragment文件,布局代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.swun.jkt.ChangeTab" >
<!-- 上部actionbar部分 -->
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="50dp"
android:background="#cc2c72d0" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:textSize="20sp"
android:background="#00000000"
android:textColor="#FFFFFFFF"
android:text="消息中心" />
<ImageButton
android:id="@+id/id_back"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:layout_marginTop="4dp"
android:layout_marginLeft="10dp"
android:background="#00000000"
android:src="@drawable/back_1" />
<ImageView
android:id="@+id/id_sms"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="15dp"
android:src="@drawable/ic_menu_end_conversation" />
</RelativeLayout>
<!-- ViewPager部分 -->
<android.support.v4.view.ViewPager
android:id="@+id/id_vp_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</pre><pre name="code" class="java">
</pre><pre name="code" class="java"><span style="white-space:pre"> </span><pre name="code" class="java"><!-- 底部自定义Tab选项部分 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<com.swun.jkt.view.ColorChangeTab
xmlns:colortab="http://schemas.android.com/apk/res/com.swun.jkt"
android:id="@+id/aty_msgcenter_msg"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_weight="1"
android:padding="5dp"
colortab:text_change_tab = "聊天"
colortab:icon_color_change_tab="@drawable/ic_launcher"
colortab:color_change_tab="#ff45c01a" />
<com.swun.jkt.view.ColorChangeTab
xmlns:colortab="http://schemas.android.com/apk/res/com.swun.jkt"
android:id="@+id/aty_msgcenter_message"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_weight="1"
android:padding="5dp"
colortab:icon_color_change_tab="@drawable/ic_msg_new"
colortab:color_change_tab="#ff45c01a"
colortab:text_change_tab = "消息" />
</LinearLayout>
</LinearLayout>
</pre><pre name="code" class="java"><span style="white-space:pre"> </span>2、java代码如下:
<span style="white-space:pre"> </span><pre name="code" class="java">package com.swun.jkt;
import java.util.ArrayList;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.View;
import com.swun.jkt.Adapter.MsgFragmentAdapter;
import com.swun.jkt.fragment.IMFragment;
import com.swun.jkt.fragment.MsgFragment;
import com.swun.jkt.fragment.MsgFragment_;
import com.swun.jkt.view.ColorChangeTab;
public class ChangeTab extends FragmentActivity {
private ColorChangeTab tab1;
private ColorChangeTab tab2;
/**
* ViewPager容器对象,用于填充Fragment对象
*/
private ViewPager content_vp;
/**
* 将多个Fragment分页对象放进集合中
*/
private ArrayList<Fragment> fragment_ls;
/**
* 用于存放底部几个Tab选项
*/
private ArrayList<ColorChangeTab> tab_ls;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_change_tab);
findView();
fragment_ls = new ArrayList<Fragment>();
tab_ls = new ArrayList<ColorChangeTab>();
tab_ls.add(tab1);
tab_ls.add(tab2);
MsgFragment mMsgFragment = new MsgFragment_();
fragment_ls.add(mMsgFragment);
IMFragment mImFragment = new IMFragment();
fragment_ls.add(mImFragment);
MsgFragmentAdapter mMsgFragmentAdapter = new MsgFragmentAdapter(getSupportFragmentManager(), fragment_ls);
content_vp.setAdapter(mMsgFragmentAdapter);
// 自定义控件的点击事件监听器
onColorTabsClickListener colorTabsClickListener = new onColorTabsClickListener();
// 为所有自定义控件设置监听器
for (ColorChangeTab tab : tab_ls) {
tab.setOnClickListener(colorTabsClickListener);
}
tab_ls.get(0).setIconAlpha(1);
// ViewPager的监听器
viewPagerListener viewPagerLis = new viewPagerListener();
// 给ViewPager设置监听器
content_vp.setOnPageChangeListener(viewPagerLis);
}
/**
*
*/
private void findView() {
tab1 = (ColorChangeTab)findViewById(R.id.aty_msgcenter_msg);
tab2 = (ColorChangeTab)findViewById(R.id.aty_msgcenter_message);
content_vp = (ViewPager)findViewById(R.id.id_vp_content);
}
private class onColorTabsClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
resetTabColor();
int ID = v.getId();
switch (ID) {
case R.id.aty_msgcenter_msg:
content_vp.setCurrentItem(0, false);
tab_ls.get(0).setIconAlpha(1);
break;
case R.id.aty_msgcenter_message:
content_vp.setCurrentItem(1, false);
tab_ls.get(1).setIconAlpha(1);
break;
}
}
}
/**
* 重置自定义控件的透明度
*/
private void resetTabColor() {
for (ColorChangeTab tab : tab_ls) {
tab.setIconAlpha(0);
}
}
private class viewPagerListener implements OnPageChangeListener {
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int position, float offset, int offsetPixels) {
updateTabColor(position, offset);
}
@Override
public void onPageSelected(int arg0) {
}
}
/**
* 当页面滑动的时候,动态更新底部Tab标签的颜色(该变暗的变暗,改变明显的变明显)
*
* @param position
* 当前第几个Fragment
* @param offset
* 当前滑动的偏移值(从0~1或者从1~0)
*/
private void updateTabColor(int position, float offset) {
if (offset > 0) {
tab_ls.get(position).setIconAlpha(1 - offset);
tab_ls.get(position + 1).setIconAlpha(offset);
}
}
}
</pre><pre name="code" class="java">
</pre><pre>