终于实现完QQ6.0的主界面。学习到的知识点也较多,也注意到自己很多要学习的。
参考资料:
http://www.imooc.com/learn/273 视频学习
http://byandby.iteye.com/blog/825330 android Canvas类介绍
http://www.cnblogs.com/feisky/archive/2010/01/10/1643460.html Android Bitmap和Canvas学习笔记
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android Canvas绘图详解(图文)
http://trylovecatch.iteye.com/blog/1189452 android 画图之setXfermode
http://blog.csdn.net/stevenhu_223/article/details/9705173 解决android4.0系统中菜单(Menu)添加Icon无效问题
*总的思路是,自定义控件,提供一个公共的接口实现图标和文字的渐变实现,而这通过设置透明度值即可。不同形状的图标充满颜色,
运用到是 Paint#setXfermode(new PorterDuffXfermode(Mode mode)) 这种多样的图片处理。外部通过ViewPager的监听得到滚动方向
和距离的数据,通过公共方法设置alpha值即可实现。
*首先完成ActionBar的实现
主要是运用反射使 ①系统显示浮动菜单提示 ②菜单带图标显示。
(a)使溢出浮动菜单的三个竖点换成自己的图标,在 style.xml中定义
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
<item name="android:actionOverflowButtonStyle">@style/ActionBarOverflowIconStyle</item>
</style>
<style name="ActionBarOverflowIconStyle">
<item name="android:src">@drawable/actionbar_add_icon</item>
</style>
(b)设置ActionBar显示溢出菜单
/**
* 设置显示溢出更多菜单 运用反射,更改 ViewConfiguration中一个字段的值
*/
private void setOverflow() {
//ViewConfiguration
//Contains methods to standard constants used in the UI
//for timeouts, sizes, and distances.
ViewConfiguration viewConfig = ViewConfiguration.get(this);
try {
Field field = viewConfig.getClass().getDeclaredField(
"sHasPermanentMenuKey");
field.setAccessible(true); // Setting this flag to false will enable
// access checks, setting to true will
// disable them.
field.setBoolean(viewConfig, false);
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
(c)设置菜单带图标
①下面两个得到方法对象的区别
Class#getMethod() -->
Returns a Method object which represents the public method with the specified name and parameter types.
Class#getDeclaredMethod()-->
Returns a Method object which represents the method matching the specified name and parameter types that is declared by the class represented by this Class.
/**
* 设置菜单选项显示图标 MenuBuild方法setOptionalIconsVisible传入true参数 运用反射实现
*/
private void initMenuIcon(Menu menu) {
// Class c = MenuBuild.class;
try {
Class c = menu.getClass();
// Method m1 = c.getMethod("setOptionalIconsVisible", Boolean.class);
Method m = c.getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
m.setAccessible(true);
m.invoke(menu, true);
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
(d)再监听菜单点击
public boolean onOptionsItemSelected(MenuItem item) ;
(e)这样,ActionBar就完成了
*完成自定义控件
完成自定义控件时,遇到好一些问题。首先记录说明这几点
①
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
这个函数是完成一些测量边距、控件大小等
会奇怪的是:
Bitmap m = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
Config.ARGB_8888);
当 创建位图是,尝试在里边获取 getMeasuredWidth() 和 getMeasuredHeight() 时,(xml)会提示错误,好像是大小必须大于0,只好把它
放到 onDraw()中去执行
②
当写好继承自 View 的自定义控件是,发现不能绘画出来,onDraw()根本没有调用。调了半天,然后经过比对,和demo实验,发现原因:layout_width 或 layout_height
为 0 时,onDraw()不会调用,invalidate()无效。
③
Canvas#drawText()时,文字的位置取决于Paint#setTextAlign()
当参数设为:Paint.Align.CENTER
它指的是底边正中心。例如,设为 (100, 100),则 Hello.World 中的中间的小点就为 (100, 100)
④Paint#setXfermode(),找资料时,发现都展示一幅示意图,纳闷这是哪里来的,文档中没有。后来发现,这是在 示例代码的 APIDemo,这也是
要学习的。
实例:
Bitmap dst = Bitmap.createBitmap(400, 400, Config.ARGB_8888);
Canvas c1 = new Canvas(dst);
Paint p1 = new Paint();
p1.setColor(Color.BLUE);
c1.drawOval(new RectF(0, 0, 300, 300), p1);
Bitmap src = Bitmap.createBitmap(400, 400, Config.ARGB_8888);
Canvas c2 = new Canvas(src);
Paint p2 = new Paint();
p2.setColor(Color.YELLOW);
c2.drawRect(150, 150, 400, 400, p2);
Paint paint = new Paint();
canvas.translate(10, 10);
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(dst, 0, 0, null);
// paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
canvas.drawBitmap(src, 0, 0, paint);
canvas.translate(10, 400);
Bitmap bitmap = Bitmap.createBitmap(400, 400, Config.ARGB_8888);
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
Canvas c3 = new Canvas(bitmap);
c3.drawBitmap(dst, 0, 0, null);
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
c3.drawBitmap(src, 0, 0, paint);
canvas.drawBitmap(bitmap, 0, 0, null);
⑤
很奇怪,颜色没有渐变。原来顺序错误,没有意识到 setColor()时,也设置了Alpha (ARGB)
(a)
代码:
/**
* 自定义控件 1、获取属性 2、得到相关高度 3、绘画
*
* @author Administrator
*
*/
public class BottomItemView1 extends View {
private static final String TAG = "BottomItemView1";
// 相关属性
private Bitmap iconBitmap;
private int color;
private int textSize;
private String textString;
/**
* 图片大小
*/
private int iconWidth;
private Rect iconRect;
private Rect textBoundRect;
private Paint textPaint;
private int textCenterX;
private int textCenterY;
/**
* 透明度 0x00-->0xFF,完全透明到完全不透明
*/
private int alpha = 0x00;
public void setAlphaAndReDraw(int alpha) {
this.alpha = alpha % 256;
if (Looper.getMainLooper() == Looper.myLooper()) {
invalidate();
} else {
postInvalidate();
}
}
public BottomItemView1(Context context) {
this(context, null);
// TODO Auto-generated constructor stub
}
public BottomItemView1(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// TODO Auto-generated constructor stub
}
public BottomItemView1(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Log.e(TAG,
"BottomItemView1(Context context, AttributeSet attrs, int defStyle);");
initAttrs(context, attrs);
initRes();
}
private void initRes() {
textBoundRect = new Rect();
textPaint = new Paint();
textPaint.setColor(color);
textPaint.setTextSize(textSize);
textPaint.setTextAlign(Paint.Align.CENTER);
iconRect = new Rect();
}
/**
* 获取相关属性
*/
private void initAttrs(Context context, AttributeSet set) {
TypedArray ta = context.obtainStyledAttributes(set,
R.styleable.BottomView);
iconBitmap = ((BitmapDrawable) ta
.getDrawable(R.styleable.BottomView_icon)).getBitmap();
color = ta.getColor(R.styleable.BottomView_color,
Color.parseColor("#ff76B34D"));
textSize = (int) ta.getDimension(R.styleable.BottomView_text_size,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12,
context.getResources().getDisplayMetrics()));
textString = ta.getString(R.styleable.BottomView_text);
ta.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.e(TAG, "onMeasure()");
modifyRes();
}
private void modifyRes() {
textPaint.getTextBounds(textString, 0, textString.length(),
textBoundRect);
int minWidth = getMeasuredWidth() - getPaddingLeft()
- getPaddingRight();
int minHeight = getMeasuredHeight() - getPaddingBottom()
- getPaddingTop() - textBoundRect.height();
iconWidth = Math.min(minWidth, minHeight);
int iconStartX = getMeasuredWidth() / 2 - iconWidth / 2
+ (getPaddingLeft() - getPaddingRight());
int iconStartY = getPaddingTop();
iconRect.set(iconStartX, iconStartY, iconStartX + iconWidth, iconStartY
+ iconWidth);
textCenterX = getMeasuredWidth() / 2;
textCenterY = iconWidth + textBoundRect.height();
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
Log.e(TAG, "onDraw()");
initCanvas(canvas, alpha);
}
private void initCanvas(Canvas canvas, int alpha) {
Bitmap m = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
Config.ARGB_8888);
Canvas c = new Canvas(m);
Paint p = new Paint();
p.setColor(color);
p.setAlpha(alpha);
c.drawRect(iconRect, p);
p.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
c.drawBitmap(iconBitmap, null, iconRect, p);
canvas.drawBitmap(iconBitmap, null, iconRect, null);
canvas.drawBitmap(m, 0, 0, null);
// 绘制原文本
textPaint.setColor(Color.GRAY);
canvas.drawText(textString, textCenterX, textCenterY, textPaint);
// 绘制变色文本
/*
* Set the paint's color. Note that the color is an int containing alpha
* as well as r,g,b. This 32bit value is not premultiplied, meaning that
* its alpha can be any value, regardless of the values of r,g,b. See
* the Color class for more details.
*/
textPaint.setColor(color);
textPaint.setAlpha(alpha);
canvas.drawText(textString, textCenterX, textCenterY, textPaint);
}
}
*产生颜色渐变效果
设置ViewPager的回调监听函数,在
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
方法中,找到 position 的规律,然后设置透明度alpha值
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.e(TAG, "-----------onPageScrolled()----------");
Log.e(TAG, "postion-->"+position);
Log.e(TAG, "positionOffset-->"+positionOffset);
Log.e(TAG, "positionOffsetPixels-->"+positionOffsetPixels);
tabEffect(position, positionOffset, positionOffsetPixels);
}
/**
* 自定义控件颜色渐变
* @param position
* @param positionOffset
* @param positionOffsetPixels
*/
private void tabEffect(int position, float positionOffset, int positionOffsetPixels) {
if (position == 0) {
leftTab = tab1;
rightTab = tab2;
} else if (position == 1) {
leftTab = tab2;
rightTab = tab3;
} else if (position == 2) {
leftTab = tab3;
rightTab = tab4;
} else if (position == 3 ) {
leftTab = tab4;
rightTab = tab4;
}
int alpha = (int) (positionOffset * 0xFF);
rightTab.setAlphaAndReDraw(alpha);
leftTab.setAlphaAndReDraw(0xFf-alpha);
}
*其中遇到的 “refresh external folder”的进程提示,总是显示在 99%,编译极为不方便。解决的方法是,取消源代码的关联
*源码:http://download.csdn.net/detail/learn2012/8467565