編輯:關於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 List tags = 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實現
本文實例為大家分享了Android控件ImageSwitcher實現引導界面的代碼,供大家參考,具體內容如下效果圖:布局代碼:<?xml version=1
華為麥芒5高配版怎麼樣 高配版和標配版有什麼區別
麥芒5的正面仍堅持了大黑邊的設計風格,真的很華為,那麼新款的華為麥芒5標配版和高配版有什麼區別呢?高配版如何?讓我們一起來看看吧!華為麥芒5標配版和高配版區
Android4.2.2 SurfaceFlinger之Layer和Bufferqueue的創建過程
本文均屬自己閱讀源碼的點滴總結,轉賬請注明出處謝謝。 歡迎和大家交流。qq:1037701636 email:gzzaigcn2012@gmail.com Androi
Android開發-DesignDemo-AndroidStudio(九)FloatingActionButton(1)
簡單對比FloatingActionButton和ImageButton的區別:左邊是ImageButton,右邊是FloatingActionButton:activi