編輯:關於Android編程
最近總感覺寫博客的激情不高,不知道為啥。放上效果圖,demo在最下面
圖上那個切換按鈕的作用呢,就是模擬改變標簽的個數動態變化整個控件的高度。
其實這個控件也算很簡單的控件了。關鍵點只有兩個
如何控制標簽自動換行切換數據源時動態改變控件的高度 再簡單的控件也需要一點一點的碼出來,咱就從最基礎的屬性設置開始。
public FlowTagView textColor(int defaultColor, int selectedColor){ this.textColorDefault = defaultColor; this.textColorSelected = selectedColor; return this; } public FlowTagView textSize(int textSize){ this.textSize = textSize; return this; } public FlowTagView backgroundColor(int defaultColor, int selectedColor){ this.backgroundColorDefault = defaultColor; this.backgroundColorSelected = selectedColor; return this; } public FlowTagView padding(int horizontalPadding, int verticalPadding, int textHorizontalPadding){ this.horizontalPadding = horizontalPadding; this.verticalPadding = verticalPadding; this.textHorizontalPadding = textHorizontalPadding; return this; } public FlowTagView itemHeight(int height){ this.itemHeight = height; return this; } public FlowTagView datas(String[] datas){ this.datas = datas; return this; } public FlowTagView listener(OnTagSelectedListener listener){ this.listener = listener; return this; }
//常亮默認值,這些參數若不調用方法傳遞,則直接使用默認值 public static final int ROUND_RADIUS = 30; public static final int TEXT_COLOR_DEFAULT = Color.BLACK; public static final int TEXT_COLOR_SELECTED = Color.WHITE; public static final int TEXT_SIZE = 30; public static final int BACKGROUND_COLOR_DEFAULT = Color.GRAY; public static final int BACKGROUND_COLOR_SELECTED = Color.GREEN; public static final int HORIZONTAL_PADDING = 30; public static final int VERTICAL_PADDING = 30; public static final int TEXT_HORIZONTAL_PADDING = 30; public static final int ITEM_HEIGHT = 60; private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private int textColorDefault = TEXT_COLOR_DEFAULT; private int textColorSelected = TEXT_COLOR_SELECTED; private int textSize = TEXT_SIZE; private int backgroundColorDefault = BACKGROUND_COLOR_DEFAULT; private int backgroundColorSelected = BACKGROUND_COLOR_SELECTED; //Tag之間的橫向和縱向的間隔 private int horizontalPadding = HORIZONTAL_PADDING; private int verticalPadding = VERTICAL_PADDING; //每個Tag內部的橫向間隔 private int textHorizontalPadding = TEXT_HORIZONTAL_PADDING; //每個Tag的高度 private int itemHeight = ITEM_HEIGHT;
public class Tag{ //文本屬性 public String text; public int textColorDefault; public int textColorSelected; public int backgroundColorDefault; public int backgroundColorSelected; public boolean isSelected; public Paint paint; //文本的繪制起點 public int drawX; public int drawY; //整個Tag占用的坐標范圍 public RectF rect = new RectF(); public Tag(String text, int textSize, int textColorDefault, int textColorSelected,
int backgroundColorDefault, int backgroundColorSelected, Paint paint, int height, int horizontalPadding, int startX, int startY){ this.text = text; this.textColorDefault = textColorDefault; this.textColorSelected = textColorSelected; this.backgroundColorDefault = backgroundColorDefault; this.backgroundColorSelected = backgroundColorSelected; this.paint = paint; //求出整個Tag的寬度 paint.setTextSize(textSize); int textWidth = (int)paint.measureText(text); int width = textWidth + 2 * horizontalPadding; //計算坐標范圍,startX,staryY是指左上角的起點 rect.left = startX; rect.top = startY; rect.right = startX + width; rect.bottom = startY + height; //計算居中繪制時的繪制起點 drawX = startX + horizontalPadding; Paint.FontMetrics metrics = paint.getFontMetrics(); drawY = (int)(startY + height / 2 + (metrics.bottom - metrics.top) / 2 - metrics.bottom); } public void draw(Canvas canvas){ if(isSelected){ //繪制背景 paint.setColor(backgroundColorSelected); paint.setStyle(Paint.Style.FILL); canvas.drawRoundRect(rect, ROUND_RADIUS, ROUND_RADIUS, paint); //繪制文本 paint.setColor(textColorSelected); canvas.drawText(text, drawX, drawY, paint); }else{ //繪制背景 paint.setColor(backgroundColorDefault); paint.setStyle(Paint.Style.STROKE); canvas.drawRoundRect(rect, ROUND_RADIUS, ROUND_RADIUS, paint); //繪制文本 paint.setColor(textColorDefault); canvas.drawText(text, drawX, drawY, paint); } } }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ int width = MeasureSpec.getSize(widthMeasureSpec); //算出繪制起點 startX = getPaddingLeft(); startY = getPaddingTop(); tags.clear(); for(int i = 0; i < datas.length; i++){ //判斷是否越過邊界 if(startX + getRealWidth(paint, textSize, datas[i], textHorizontalPadding) + horizontalPadding
> width - getPaddingRight()){ //在下一行開始繪制 startX = getPaddingLeft(); startY += itemHeight + verticalPadding; } Tag tag = new Tag(datas[i], textSize, textColorDefault, textColorSelected, backgroundColorDefault, backgroundColorSelected, paint, itemHeight, textHorizontalPadding, startX, startY); tags.add(tag); //動態更新值 startX += getRealWidth(paint, textSize, datas[i], textHorizontalPadding) + horizontalPadding; } //算出整個控件需要的高度 int height = startY + itemHeight + getPaddingBottom(); setMeasuredDimension(width, height); }這裡用到了一個工具方法getRealWidth,這個就是用來計算每一個標簽的真實寬度的。
/** * 根據參數算出某個Tag所需要占用的寬度值,包括內補白 */ public static int getRealWidth(Paint paint, int textSize, String text, int textHorizontalPadding){ paint.setTextSize(textSize); int textWidth = (int)paint.measureText(text); return textWidth + 2 * textHorizontalPadding; }
public void commit(){ if(datas == null){ Log.e("FlowTagView", "maybe not invok the method named datas(String[])"); throw new IllegalStateException("maybe not invok the method named datas(String[])"); } paint.setTextSize(textSize); if(datas.length != tags.size()){ //重新實例化 ViewGroup.LayoutParams params = getLayoutParams(); setLayoutParams(params); } }
public interface OnTagSelectedListener{ void onTagSelected(FlowTagView view, int position); }
//點擊事件的滑動距離阈值 private int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); //ACTION_DOWN時的坐標值 private float mTouchX; private float mTouchY; //ACTION_DOWN時選中的tag的索引 private int mTouchPosition;onTouchEvent方法進行事件分發。
@Override public boolean onTouchEvent(MotionEvent event){ switch(event.getAction()){ case MotionEvent.ACTION_DOWN: mTouchX = event.getX(); mTouchY = event.getY(); mTouchPosition = getTagPosition(mTouchX, mTouchY); return true; case MotionEvent.ACTION_UP: float mUpX = event.getX(); float mUpY = event.getY(); //滑動距離小於點擊阈值並且點擊時的索引值不是非法值,並且up時的索引值和down時的索引值相等時,才觸發選中操作 if(Math.abs(mUpX - mTouchX) < mTouchSlop && Math.abs(mUpY - mTouchY) < mTouchSlop && mTouchPosition != -1 && getTagPosition(mUpX, mUpY) == mTouchPosition){ //觸發點擊選中 setSelect(mTouchPosition); } break; } return super.onTouchEvent(event); }
/** * 根本坐標值,返回對應的tag的索引,若不存在則返回-1 */ private int getTagPosition(float x, float y){ for(int i = 0; i < tags.size(); i++){ if(tags.get(i).rect.contains(x, y)){ return i; } } return -1; } public void setSelect(int position){ if(position < 0 || position >= tags.size()){ Log.e("FlowTagView", "the position is illetal"); throw new IllegalArgumentException("the position is illetal"); } for(int i = 0; i < tags.size(); i++){ //關閉其他選擇 if(i != position){ tags.get(i).isSelected = false; }else{ tags.get(i).isSelected = true; } } //觸發監聽器 if(listener != null){ listener.onTagSelected(this, position); } //必須要刷新UI invalidate(); } public int getSelect(){ for(int i = 0; i < tags.size(); i++){ if(tags.get(i).isSelected){ return i; } } return -1; }
package cc.wxf.component; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; /** * Created by ccwxf on 2016/7/21. */ public class FlowTagView extends View { //常亮默認值,這些參數若不調用方法傳遞,則直接使用默認值 public static final int ROUND_RADIUS = 30; public static final int TEXT_COLOR_DEFAULT = Color.BLACK; public static final int TEXT_COLOR_SELECTED = Color.WHITE; public static final int TEXT_SIZE = 30; public static final int BACKGROUND_COLOR_DEFAULT = Color.GRAY; public static final int BACKGROUND_COLOR_SELECTED = Color.GREEN; public static final int HORIZONTAL_PADDING = 30; public static final int VERTICAL_PADDING = 30; public static final int TEXT_HORIZONTAL_PADDING = 30; public static final int ITEM_HEIGHT = 60; private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private int textColorDefault = TEXT_COLOR_DEFAULT; private int textColorSelected = TEXT_COLOR_SELECTED; private int textSize = TEXT_SIZE; private int backgroundColorDefault = BACKGROUND_COLOR_DEFAULT; private int backgroundColorSelected = BACKGROUND_COLOR_SELECTED; //Tag之間的橫向和縱向的間隔 private int horizontalPadding = HORIZONTAL_PADDING; private int verticalPadding = VERTICAL_PADDING; //每個Tag內部的橫向間隔 private int textHorizontalPadding = TEXT_HORIZONTAL_PADDING; //每個Tag的高度 private int itemHeight = ITEM_HEIGHT; //tag的繪制起點,動態計算得值 private int startX; private int startY; //Tag顯示的文本 private String[] datas; private Listtags = new ArrayList (); //點擊事件的滑動距離阈值 private int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); //ACTION_DOWN時的坐標值 private float mTouchX; private float mTouchY; //ACTION_DOWN時選中的tag的索引 private int mTouchPosition; private OnTagSelectedListener listener; public FlowTagView(Context context, AttributeSet attrs, int defStyleAttr){ super(context, attrs, defStyleAttr); } public FlowTagView(Context context, AttributeSet attrs){ super(context, attrs); } public FlowTagView(Context context){ super(context); } public FlowTagView textColor(int defaultColor, int selectedColor){ this.textColorDefault = defaultColor; this.textColorSelected = selectedColor; return this; } public FlowTagView textSize(int textSize){ this.textSize = textSize; return this; } public FlowTagView backgroundColor(int defaultColor, int selectedColor){ this.backgroundColorDefault = defaultColor; this.backgroundColorSelected = selectedColor; return this; } public FlowTagView padding(int horizontalPadding, int verticalPadding, int textHorizontalPadding){ this.horizontalPadding = horizontalPadding; this.verticalPadding = verticalPadding; this.textHorizontalPadding = textHorizontalPadding; return this; } public FlowTagView itemHeight(int height){ this.itemHeight = height; return this; } public FlowTagView datas(String[] datas){ this.datas = datas; return this; } public FlowTagView listener(OnTagSelectedListener listener){ this.listener = listener; return this; } public void commit(){ if(datas == null){ Log.e("FlowTagView", "maybe not invok the method named datas(String[])"); throw new IllegalStateException("maybe not invok the method named datas(String[])"); } paint.setTextSize(textSize); if(datas.length != tags.size()){ //重新實例化 ViewGroup.LayoutParams params = getLayoutParams(); setLayoutParams(params); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ int width = MeasureSpec.getSize(widthMeasureSpec); //算出繪制起點 startX = getPaddingLeft(); startY = getPaddingTop(); tags.clear(); for(int i = 0; i < datas.length; i++){ //判斷是否越過邊界 if(startX + getRealWidth(paint, textSize, datas[i], textHorizontalPadding) + horizontalPadding > width - getPaddingRight()){ //在下一行開始繪制 startX = getPaddingLeft(); startY += itemHeight + verticalPadding; } Tag tag = new Tag(datas[i], textSize, textColorDefault, textColorSelected, backgroundColorDefault, backgroundColorSelected, paint, itemHeight, textHorizontalPadding, startX, startY); tags.add(tag); //動態更新值 startX += getRealWidth(paint, textSize, datas[i], textHorizontalPadding) + horizontalPadding; } //算出整個控件需要的高度 int height = startY + itemHeight + getPaddingBottom(); setMeasuredDimension(width, height); } /** * 根據參數算出某個Tag所需要占用的寬度值,包括內補白 */ public static int getRealWidth(Paint paint, int textSize, String text, int textHorizontalPadding){ paint.setTextSize(textSize); int textWidth = (int)paint.measureText(text); return textWidth + 2 * textHorizontalPadding; } @Override protected void onDraw(Canvas canvas){ //繪制代理 for(int i = 0; i < tags.size(); i++){ tags.get(i).draw(canvas); } } @Override public boolean onTouchEvent(MotionEvent event){ switch(event.getAction()){ case MotionEvent.ACTION_DOWN: mTouchX = event.getX(); mTouchY = event.getY(); mTouchPosition = getTagPosition(mTouchX, mTouchY); return true; case MotionEvent.ACTION_UP: float mUpX = event.getX(); float mUpY = event.getY(); //滑動距離小於點擊阈值並且點擊時的索引值不是非法值,並且up時的索引值和down時的索引值相等時,才觸發選中操作 if(Math.abs(mUpX - mTouchX) < mTouchSlop && Math.abs(mUpY - mTouchY) < mTouchSlop && mTouchPosition != -1 && getTagPosition(mUpX, mUpY) == mTouchPosition){ //觸發點擊選中 setSelect(mTouchPosition); } break; } return super.onTouchEvent(event); } /** * 根本坐標值,返回對應的tag的索引,若不存在則返回-1 */ private int getTagPosition(float x, float y){ for(int i = 0; i < tags.size(); i++){ if(tags.get(i).rect.contains(x, y)){ return i; } } return -1; } public void setSelect(int position){ if(position < 0 || position >= tags.size()){ Log.e("FlowTagView", "the position is illetal"); throw new IllegalArgumentException("the position is illetal"); } for(int i = 0; i < tags.size(); i++){ //關閉其他選擇 if(i != position){ tags.get(i).isSelected = false; }else{ tags.get(i).isSelected = true; } } //觸發監聽器 if(listener != null){ listener.onTagSelected(this, position); } //必須要刷新UI invalidate(); } public int getSelect(){ for(int i = 0; i < tags.size(); i++){ if(tags.get(i).isSelected){ return i; } } return -1; } public class Tag{ //文本屬性 public String text; public int textColorDefault; public int textColorSelected; public int backgroundColorDefault; public int backgroundColorSelected; public boolean isSelected; public Paint paint; //文本的繪制起點 public int drawX; public int drawY; //整個Tag占用的坐標范圍 public RectF rect = new RectF(); public Tag(String text, int textSize, int textColorDefault, int textColorSelected, int backgroundColorDefault, int backgroundColorSelected, Paint paint, int height, int horizontalPadding, int startX, int startY){ this.text = text; this.textColorDefault = textColorDefault; this.textColorSelected = textColorSelected; this.backgroundColorDefault = backgroundColorDefault; this.backgroundColorSelected = backgroundColorSelected; this.paint = paint; //求出整個Tag的寬度 paint.setTextSize(textSize); int textWidth = (int)paint.measureText(text); int width = textWidth + 2 * horizontalPadding; //計算坐標范圍,startX,staryY是指左上角的起點 rect.left = startX; rect.top = startY; rect.right = startX + width; rect.bottom = startY + height; //計算居中繪制時的繪制起點 drawX = startX + horizontalPadding; Paint.FontMetrics metrics = paint.getFontMetrics(); drawY = (int)(startY + height / 2 + (metrics.bottom - metrics.top) / 2 - metrics.bottom); } public void draw(Canvas canvas){ if(isSelected){ //繪制背景 paint.setColor(backgroundColorSelected); paint.setStyle(Paint.Style.FILL); canvas.drawRoundRect(rect, ROUND_RADIUS, ROUND_RADIUS, paint); //繪制文本 paint.setColor(textColorSelected); canvas.drawText(text, drawX, drawY, paint); }else{ //繪制背景 paint.setColor(backgroundColorDefault); paint.setStyle(Paint.Style.STROKE); canvas.drawRoundRect(rect, ROUND_RADIUS, ROUND_RADIUS, paint); //繪制文本 paint.setColor(textColorDefault); canvas.drawText(text, drawX, drawY, paint); } } } public interface OnTagSelectedListener{ void onTagSelected(FlowTagView view, int position); } }
package cc.wxf.androiddemo; import android.app.Activity; import android.content.res.Resources; import android.os.Bundle; import android.view.View; import android.widget.Toast; import cc.wxf.component.FlowTagView; public class MainActivity extends Activity { private int i = 0; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final String[] datas1 = new String[]{ "推薦", "電影", "電視劇", "頭條", "娛樂", "動漫", "猜你喜歡", "資訊", "搞笑", "體育", "綜藝", "片花", "少兒", "今日頭條", "娛樂", "動漫", "猜你喜歡", "資訊", "搞笑", "體育", "綜藝" }; final String[] datas2 = new String[]{ "推薦", "電影", "電視劇", "頭條", "娛樂", "動漫", "猜你喜歡", "資訊" }; Resources resources = getResources(); final FlowTagView tagView = (FlowTagView) findViewById(R.id.tagView); tagView.datas(datas1) //下面的5個方法若不設置,則會采用默認值 .textColor(resources.getColor(android.R.color.darker_gray), resources.getColor(android.R.color.white)) .textSize(sp2px(15)) .backgroundColor(resources.getColor(android.R.color.darker_gray), resources.getColor(android.R.color.holo_green_light)) .itemHeight(dp2px(40)) .padding(dp2px(10), dp2px(10), dp2px(15)) //上面的5個方法若不設置,則會采用默認值 .listener(new FlowTagView.OnTagSelectedListener() { @Override public void onTagSelected(FlowTagView view, int position) { Toast.makeText(MainActivity.this, "選中了:" + position, Toast.LENGTH_SHORT).show(); } }) //commit必須調用 .commit(); //模擬標簽的個數發生變化,造成控件的自動伸展 findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { i ++; //commit必須調用 tagView.datas(i % 2 == 0 ? datas1 : datas2).commit(); } }); } public int sp2px(int sp){ float density = getResources().getDisplayMetrics().scaledDensity; return (int) (sp * density + 0.5f); } public int dp2px(int dp){ float density = getResources().getDisplayMetrics().density; return (int) (dp * density + 0.5f); } }
本文實例為大家分享了Android控件ImageSwitcher實現引導界面的代碼,供大家參考,具體內容如下效果圖:布局代碼:<?xml version=1
麥芒5的正面仍堅持了大黑邊的設計風格,真的很華為,那麼新款的華為麥芒5標配版和高配版有什麼區別呢?高配版如何?讓我們一起來看看吧!華為麥芒5標配版和高配版區
本文均屬自己閱讀源碼的點滴總結,轉賬請注明出處謝謝。 歡迎和大家交流。qq:1037701636 email:[email protected] Androi
簡單對比FloatingActionButton和ImageButton的區別:左邊是ImageButton,右邊是FloatingActionButton:activi