編輯:關於Android編程
著手開發一款應用的時候,設置或者菜單頁面是可能需要的,但是,那重復的布局會很令人苦惱。新手可能會一項項的重復繪制,有經驗的你或許會用到include,或者用到組合控件。除了以上的方法之外,閒來無事,寫了一個通用的View(MenuItemView)。此view暫時可以展示兩種功能,一是通用的項,另一種是帶開關的項,截圖如下:
1.自定義屬性:attrs.xml<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
2.MenuItemView.java源碼:
package com.dandy.weight;
/**
* Created by Administrator on 2016/7/20.
*/
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import com.dandy.album.R;
/**
* 設置,菜單中的布局項
* 默認控件是縱向居中顯示,所有paddingTop和paddingBottom在這沒有作用
* Created by dandy on 2016/4/14.
*/
public class MenuItemView extends View{
private static final String TAG = "MenuItemView";
/**默認控件的高**/
private static final int HEIGHT_DEFAULT = 50;
/**文字繪制時默認大小**/
private static final int TEXTSZIE_DEFAULT = 14;
/**尾部箭頭圖標大小**/
private static final int ARROW_SIZE = 13;
/***SwitchButton默認寬*/
private static final int SWITCHBUTTON_WIDTH = 50;
/***SwitchButton默認高*/
private static final int SWITCHBUTTON_HEIGHT = 28;
private static final long DELAYDURATION = 10;
/**頭標**/
private Drawable headerDrawable;
/**頭標大小,寬高一致**/
private int headDrawableSize = -1;
/**頭標寬**/
private int headerDrawableWidth;
/**頭標高**/
private int headerDrawableHeight;
/**繪制頭標時,畫布在Y軸的繪制偏移量**/
private float headerDrawableStartDrawY;
/**文字與圖片間的距離**/
private int drawablePadding = 0;
/**頭部文字提示**/
private String textHeader;
/**文字顏色**/
private int textHeaderColor = Color.parseColor("#5a5a5a");
/**文字大小**/
private int textSize = -1;
/**繪制文字的畫筆**/
private Paint textPaint;
/** 尾部 > 圖片**/
private Drawable arrowDrawable;
/**尾部 > 大小**/
private int arrowSize = -1;
/** > 繪制的X軸偏移量**/
private float arrowStartDrawX;
/** > 繪制的Y軸偏移量**/
private float arrowStartDrawY;
/**尾部寬**/
private int arrowDrawableWidth;
/**尾部高**/
private int arrowDrawableHeight;
/**繪制arrow畫筆**/
private Paint arrowPaint;
/**arrowPaint 顏色**/
private int arrowColor = Color.parseColor("#5a5a5a");
private DisplayMetrics dm;
/*以下是繪制SwitchButton所用到的參數*/
private ItemType itemType = ItemType.NORMAL;
/**默認寬**/
private int toggleWidth = -1;
/**默認高**/
private int toggleHeight = -1;
/**開啟顏色**/
private int onColor = Color.parseColor("#4ebb7f");
/**關閉顏色**/
private int offColor = Color.parseColor("#dadbda");
/**灰色帶顏色**/
private int areaColor = Color.parseColor("#dadbda");
/**手柄顏色**/
private int handlerColor = Color.parseColor("#ffffff");
/**邊框顏色**/
private int borderColor = offColor;
/**開關狀態**/
private boolean toggleOn = false;
/**邊框寬**/
private int borderWidth = 2;
/**縱軸中心**/
private float centerY;
/**按鈕水平方向開始、結束的位置**/
private float startX,endX;
/**手柄x軸方向最小、最大值**/
private float handlerMinX,handlerMaxX;
/**手柄大小**/
private int handlerSize;
/**手柄在x軸的坐標位置**/
private float handlerX;
/**關閉時內部灰色帶寬度**/
private float areaWidth;
/**是否使用動畫效果**/
private boolean animate = true;
/**是否默認處於打開狀態**/
private boolean defaultOn = true;
/**按鈕半徑**/
private float radius;
/**整個switchButton的區域**/
private RectF toggleRectF = new RectF();
/**繪制switchButton的畫筆**/
private Paint togglePaint;
private OnToggleChangedListener mListener;
private double currentDelay;
private float downX = 0;
/**switchButton在X軸繪制的偏移量**/
private float switchButtonDrawStartX;
/**switchButton在Y軸繪制的偏移量**/
private float switchButtonDrawStartY;
/**分割線,默認在底部繪制**/
private Drawable dividerr;
/**分割線繪制的寬**/
private int dividerWidth = 2;
/**是否需要繪制分割線**/
private boolean dividerVisibilty = true;
private boolean isPressed = false;
public MenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setup(attrs);
}
public MenuItemView(Context context, AttributeSet attrs) {
super(context, attrs);
setup(attrs);
}
/**
* 初始化控件,獲取相關的控件屬性
* @param attrs
*/
private void setup(AttributeSet attrs){
dm = Resources.getSystem().getDisplayMetrics();
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MenuItemView);
if(typedArray != null){
int count = typedArray.getIndexCount();
for(int i = 0;i < count;i++){
int attr = typedArray.getIndex(i);
switch (attr){
case R.styleable.MenuItemView_headerDrawable:
headerDrawable = typedArray.getDrawable(attr);
break;
case R.styleable.MenuItemView_headerSize:
headDrawableSize = typedArray.getDimensionPixelSize(attr,headDrawableSize);
break;
case R.styleable.MenuItemView_drawPadding:
drawablePadding = typedArray.getDimensionPixelSize(attr, drawablePadding);
break;
case R.styleable.MenuItemView_textHeader:
textHeader = typedArray.getString(attr);
break;
case R.styleable.MenuItemView_textColor:
textHeaderColor = typedArray.getColor(attr, textHeaderColor);
break;
case R.styleable.MenuItemView_textSize:
textSize = typedArray.getDimensionPixelSize(attr, textSize);
break;
case R.styleable.MenuItemView_arrowDrawable:
arrowDrawable = typedArray.getDrawable(attr);
break;
case R.styleable.MenuItemView_arrowSize:
arrowSize = typedArray.getDimensionPixelSize(attr, arrowSize);
break;
case R.styleable.MenuItemView_arrowColor:
arrowColor = typedArray.getColor(attr, arrowColor);
break;
case R.styleable.MenuItemView_onColor:
onColor = typedArray.getColor(attr, onColor);
break;
case R.styleable.MenuItemView_offColor:
borderColor = offColor = typedArray.getColor(attr,offColor);
break;
case R.styleable.MenuItemView_areaColor:
areaColor = typedArray.getColor(attr, areaColor);
break;
case R.styleable.MenuItemView_handlerColor:
handlerColor = typedArray.getColor(attr, handlerColor);
break;
case R.styleable.MenuItemView_bordeWidth:
borderWidth = typedArray.getColor(attr, borderWidth);
break;
case R.styleable.MenuItemView_animate:
animate = typedArray.getBoolean(attr, animate);
break;
case R.styleable.MenuItemView_defaultOn:
defaultOn = typedArray.getBoolean(attr, defaultOn);
break;
case R.styleable.MenuItemView_itemType:
itemType = ItemType.getValue(typedArray.getInt(attr, ItemType.NORMAL.ordinal()));
break;
case R.styleable.MenuItemView_toggleWidth:
toggleWidth = typedArray.getDimensionPixelOffset(attr, toggleWidth);
break;
case R.styleable.MenuItemView_toggleHeight:
toggleHeight = typedArray.getDimensionPixelOffset(attr, toggleHeight);
break;
case R.styleable.MenuItemView_dividerr:
dividerr = typedArray.getDrawable(attr);
break;
case R.styleable.MenuItemView_dividerWidth:
dividerWidth = typedArray.getDimensionPixelOffset(attr,dividerWidth);
break;
case R.styleable.MenuItemView_dividerVisibilty:
dividerVisibilty = typedArray.getBoolean(attr,dividerVisibilty);
break;
}
}
typedArray.recycle();
}
if(textSize == -1){
textSize = applyDimension(TEXTSZIE_DEFAULT);
}
/**
* 初始化文字畫筆
*/
textPaint = new Paint();
textPaint.setColor(textHeaderColor);
textPaint.setTextSize(textSize);
textPaint.setAntiAlias(true);
if(itemType == ItemType.TOGGLE){
if(toggleWidth == -1){
toggleWidth = applyDimension(SWITCHBUTTON_WIDTH);
}
if(toggleHeight == -1){
toggleHeight = applyDimension(SWITCHBUTTON_HEIGHT);
}
togglePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
togglePaint.setStyle(Paint.Style.FILL);
togglePaint.setStrokeCap(Paint.Cap.ROUND);
togglePaint.setAntiAlias(true);
if(defaultOn){
currentDelay = 1;
toggleOn();
}else{
currentDelay = 0;
toggleOff();
}
}else if(itemType == ItemType.ARROW){
if(arrowSize == -1){
arrowSize = applyDimension(ARROW_SIZE);
}
arrowPaint = new Paint();
arrowPaint.setColor(arrowColor);
arrowPaint.setAntiAlias(true);
arrowPaint.setStrokeWidth(3.0f);
}
/**
* 設置默認背景色
*/
if(getBackground() == null){
setBackgroundColor(Color.parseColor("#FFFFFF"));
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if(headerDrawable != null){
if(headDrawableSize >= 0 ){
headerDrawableWidth = headerDrawableHeight = headDrawableSize;
}else{
headerDrawableWidth = headerDrawable.getIntrinsicWidth();
headerDrawableHeight = headerDrawable.getIntrinsicHeight();
}
Log.i(TAG,"[onLayout] headerDrawableWidth = "+headerDrawableWidth+",headerDrawableHeight = "+headerDrawableHeight);
headerDrawableStartDrawY = (getHeight() - headerDrawableHeight)/ 2.0f;
Log.i(TAG, "[onLayout] headerDrawableStartDrawY = " + headerDrawableStartDrawY);
}
if(itemType == ItemType.ARROW){
if(arrowDrawable != null){
arrowDrawableWidth = arrowDrawable.getIntrinsicWidth();
arrowDrawableHeight = arrowDrawable.getIntrinsicHeight();
}
if(arrowSize >= 0){
arrowDrawableWidth = arrowDrawableHeight = arrowSize;
}
Log.i(TAG, "[onLayout] arrowDrawableWidth = " + arrowDrawableWidth + ",arrowDrawableHeight = " + arrowDrawableHeight);
arrowStartDrawX = getWidth() - getPaddingRight() - arrowDrawableWidth;
arrowStartDrawY = (getHeight() - arrowDrawableHeight) / 2.0f;
Log.i(TAG, "[onLayout] arrowStartDrawX = " + arrowStartDrawX + ",arrowStartDrawY = " + arrowStartDrawY);
}else if(itemType == ItemType.TOGGLE){
radius = Math.min(toggleWidth,toggleHeight) * 0.5f;
centerY = radius;
startX = centerY;
endX = toggleWidth - radius;
handlerMinX = startX + borderWidth;
handlerMaxX = endX - borderWidth;
handlerSize = toggleHeight - 4*borderWidth;
handlerX = toggleOn?handlerMaxX:handlerMinX;
areaWidth = 0;
switchButtonDrawStartX = getWidth() - getPaddingRight() - toggleWidth;
switchButtonDrawStartY = (getHeight() - toggleHeight) / 2.0f;
Log.i(TAG, "[onLayout] switchButtonDrawStartX = " + switchButtonDrawStartX + ",switchButtonDrawStartY = " + switchButtonDrawStartY);
}
super.onLayout(changed, left, top, right, bottom);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**計算寬**/
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
if(widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST){
width = applyDimension(-1);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
}
/**計算高**/
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.AT_MOST) {
height = applyDimension(HEIGHT_DEFAULT);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
Log.i(TAG, "[onMeasure] width = " + width + ",height = " + height);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
Log.i(TAG, "[onDraw] start draw!");
if(headerDrawable != null){
drawHeaderDrawable(canvas);
}
drawHeaderText(canvas);
if (itemType == ItemType.ARROW) {
drawFooterDrawable(canvas);
}else if(itemType == ItemType.TOGGLE){
drawSwitchButton(canvas);
}
if(dividerVisibilty){
drawDivider(canvas);
}
super.onDraw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
isPressed = true;
if (itemType != ItemType.TOGGLE){
postDelayed(new Runnable() {
@Override
public void run() {
MenuItemView.this.setPressed(isPressed);
}
}, ViewConfiguration.getTapTimeout());
}else{
downX = event.getX();
}
break;
case MotionEvent.ACTION_MOVE:
isPressed = false;
this.setPressed(false);
break;
case MotionEvent.ACTION_UP:
if(itemType != ItemType.TOGGLE){
performClick();
postDelayed(new Runnable() {
@Override
public void run() {
MenuItemView.this.setPressed(false);
}
}, ViewConfiguration.getTapTimeout());
}else if(downX >= switchButtonDrawStartX){//為了擴大switchButton的響應區域
toggle();
}
break;
default:
isPressed = false;
this.setPressed(false);
break;
}
return true;
}
/**
* 繪制頭標
* @param canvas
*/
private void drawHeaderDrawable(Canvas canvas){
if(headerDrawable == null){
throw new NullPointerException("headerDrawable was setted null !");
}
canvas.save();
canvas.translate(getPaddingLeft(), headerDrawableStartDrawY);
headerDrawable.setBounds(0, 0, headerDrawableWidth, headerDrawableHeight);
headerDrawable.draw(canvas);
canvas.restore();
}
/**
* 繪制頭部文字提示
* @param canvas
*/
private void drawHeaderText(Canvas canvas){
canvas.save();
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
RectF targetRect = new RectF(getPaddingLeft() + headerDrawableWidth + drawablePadding,0,getWidth(),getHeight());
int baseLine = (int)((targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2);
canvas.translate(getPaddingLeft() + headerDrawableWidth + drawablePadding, baseLine);
canvas.drawText(textHeader, 0, 0, textPaint);
canvas.restore();
}
/**
* 繪制尾部指示圖標
*
* @param canvas
*/
private void drawFooterDrawable(Canvas canvas){
canvas.save();
canvas.translate(arrowStartDrawX, arrowStartDrawY);
if(arrowDrawable != null){
arrowDrawable.setBounds(0, 0, arrowDrawableWidth, arrowDrawableHeight);
arrowDrawable.draw(canvas);
}else{
/**
* 自行繪制arrow圖標,繪制規則是:X軸坐標在arrow中間位置開始繪制,Y軸繪制不變
*/
final float centerY = arrowDrawableHeight / 2.0f;
final float lineStartX = arrowDrawableWidth / 2.0f;
final float lineEndX = arrowDrawableWidth;
canvas.drawLine(lineStartX,0,lineEndX,centerY,arrowPaint);
canvas.drawLine(lineStartX,arrowDrawableHeight,lineEndX,centerY,arrowPaint);
}
canvas.restore();
}
/**
* 繪制switchButton
* @param canvas
*/
private void drawSwitchButton(Canvas canvas){
canvas.save();
canvas.translate(switchButtonDrawStartX,switchButtonDrawStartY);
/**繪制整個按鈕**/
toggleRectF.set(0, 0, toggleWidth, toggleHeight);
togglePaint.setColor(borderColor);
canvas.drawRoundRect(toggleRectF, radius, radius,togglePaint);
/**繪制關閉灰色區域**/
if(areaWidth > 0 ){
final float cy = areaWidth * 0.5f;
toggleRectF.set(handlerX - cy, centerY - cy, endX + cy, centerY + cy);
togglePaint.setColor(offColor);
canvas.drawRoundRect(toggleRectF,cy,cy,togglePaint);
}
/**繪制手柄**/
final float handlerRadius = handlerSize * 0.5f;
toggleRectF.set(handlerX - handlerRadius, centerY - handlerRadius, handlerX + handlerRadius, centerY + handlerRadius);
togglePaint.setColor(handlerColor);
canvas.drawRoundRect(toggleRectF, handlerRadius, handlerRadius, togglePaint);
canvas.restore();
}
/**
* 繪制分割線
* @param canvas
*/
private void drawDivider(Canvas canvas){
canvas.save();
canvas.translate(getPaddingLeft(), getHeight() - dividerWidth);
if(dividerr == null){
ShapeDrawable divider = new ShapeDrawable(new RectShape());
divider.getPaint().setStrokeWidth(dividerWidth);
divider.getPaint().setAntiAlias(true);
divider.getPaint().setColor(Color.parseColor("#CCCCCC"));
divider.setBounds(0, 0, getWidth(), dividerWidth);
divider.draw(canvas);
}else{
dividerr.setBounds(0, 0, getWidth(),dividerWidth);
dividerr.draw(canvas);
}
canvas.restore();
}
/**
* 開關狀態切換
*/
public void toggle(){
toggle(animate);
}
/**
* 開關狀態切換
* @param animate
*/
public void toggle(boolean animate){
toggleOn = !toggleOn;
takeEffect(animate);
}
/**
* 開啟狀態
*/
public void toggleOn(){
toggleOn(animate);
}
/**
* 開啟狀態
* @param animate
*/
public void toggleOn(boolean animate){
toggleOn = true;
takeEffect(animate);
}
/**
* 關閉狀態
*/
public void toggleOff(){
toggleOff(animate);
}
/**
* 關閉狀態
* @param animate
*/
public void toggleOff(boolean animate){
toggleOn = false;
takeEffect(animate);
}
/**
* 開始處理狀態切換
* @param animate
*/
private void takeEffect(boolean animate){
if(mListener != null){
mListener.onToggle(toggleOn);
}
if(animate){
postDelayed(toggleRunnable, DELAYDURATION);
}else {
caculateEffect(toggleOn ? 1 : 0);
}
}
/**
* 時時計算
* @param value
*/
private void caculateEffect(double value){
handlerX = (float)mapValueFromRangeToRange(value,0,1.0,handlerMinX,handlerMaxX);
areaWidth = (float)mapValueFromRangeToRange(1.0-value,0,1.0,10,handlerSize);
final int fb = Color.blue(onColor);
final int fr = Color.red(onColor);
final int fg = Color.green(onColor);
final int tb = Color.blue(offColor);
final int tr = Color.red(offColor);
final int tg = Color.green(offColor);
int sb = (int) mapValueFromRangeToRange(1.0 - value, 0, 1.0, fb, tb);
int sr = (int) mapValueFromRangeToRange(1.0 - value, 0, 1.0, fr, tr);
int sg = (int) mapValueFromRangeToRange(1.0 - value, 0, 1.0, fg, tg);
sb = clamp(sb, 0, 255);
sr = clamp(sr, 0, 255);
sg = clamp(sg, 0, 255);
borderColor = Color.rgb(sr, sg, sb);
postInvalidate();
}
private int clamp(int value, int low, int high) {
return Math.min(Math.max(value, low), high);
}
/**
* Map a value within a given range to another range.
* @param value the value to map
* @param fromLow the low end of the range the value is within
* @param fromHigh the high end of the range the value is within
* @param toLow the low end of the range to map to
* @param toHigh the high end of the range to map to
* @return the mapped value
*/
private double mapValueFromRangeToRange(
double value, double fromLow, double fromHigh,
double toLow, double toHigh) {
double fromRangeSize = fromHigh - fromLow;
double toRangeSize = toHigh - toLow;
double valueScale = (value - fromLow) / fromRangeSize;
return toLow + (valueScale * toRangeSize);
}
private final Runnable toggleRunnable = new Runnable() {
@Override
public void run() {
if(toggleOn){
if(currentDelay <= 1){
caculateEffect(currentDelay);
postDelayed(toggleRunnable,DELAYDURATION);
currentDelay = currentDelay + 0.1;
}else{
currentDelay = 1;
}
}else{
if(currentDelay >= 0){
caculateEffect(currentDelay);
postDelayed(toggleRunnable, DELAYDURATION);
currentDelay = currentDelay - 0.1;
}else{
currentDelay = 0;
}
}
}
};
/**
* px2dp
* @param value
*/
private int applyDimension(float value){
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,value,dm);
}
/**
* 類型
*/
private enum ItemType{
NORMAL, ARROW, TOGGLE;
public static ItemType getValue(int index){
for(ItemType type:values()){
if(type.ordinal() == index){
return type;
}
}
return NORMAL;
}
}
/**
* 設置開關監聽
*/
public void setOnToggleChangedlistener(OnToggleChangedListener listener){
this.mListener = listener;
}
/**
* 開關狀態監聽
*/
public interface OnToggleChangedListener{
/**
* 是否開啟
* @param on
*/
void onToggle(boolean on);
}
}
3.布局就不說了,和正常的View使用就是了。
本文實例介紹的是Android的Tab控件,Tab控件可以達到分頁的效果,讓一個屏幕的內容盡量豐富,當然也會增加開發的復雜程度,在有必要的時候再使用。Android的Ta
本文演示如何在Android中實現ListView圓角效果。無論是網站,還是APP,人們都愛看一些新穎的視圖效果。直角看多了,就想看看圓角,這幾年刮起了一陣陣的圓角設計風
之前一直在Android應用層上做工作,最近開始研究Android平台上的東東了,主要是在Android Frameworks層和系統庫層進行研究。以下是我自己的理解,領
本人使用Win8系統時間久了系統垃圾一大堆 ,後來重裝了Win8系統,再用ADT(adt-bunlde-windows),總會出現ddms初始化錯誤,logcat也無法