編輯:關於Android編程
效果說明:滑竿指示器,是一段彎曲的圓弧,要求在桿上,有滑動小球事件,小球會根據下標文字的起始角度與終止角度,是否選擇滑倒下一個位置。當點擊下標文字時,小球也要做出相應的指示。
1)MainActivity
package com.example.chenkui.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intIndicatorTabView();
// setContentView(R.layout.watch_view);
}
private void intIndicatorTabView() {
IndicatorTabView view = (IndicatorTabView) findViewById(R.id.myView);
List list = new ArrayList<>();
list.add("待評價");
list.add("待付款");
list.add("待發貨");
list.add("待收貨");
view.setTabInfo(list);
view.setSelection(3);//初始化選擇指示器小球位置;
view.setTabChangeListener(new IndicatorTabView.OnTabChangeListener() {
@Override
public void onTabSelected(View v, int position) {
}
});
}
}
2)IndicatorTabView
在繪制時,按照圖層的結構,先繪制底層顏色,再繪制上一層圖形,對於特殊繪制的,如旋轉畫布,移動繪制圓心坐標,一般先 canvas.save();保存已經繪制圖層,canvas.restore();//他的作用為,將之前的繪制保存的圖片save(),進行合並.
package com.example.chenkui.myapplication;
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 java.util.ArrayList;
import java.util.List;
public class IndicatorTabView extends View {
private String TAG = IndicatorTabView.class.getSimpleName();
private static final float DEFAULT_SWEEP_ANGLE = 48.0f;
private float mSweepAngle = DEFAULT_SWEEP_ANGLE;
private float mStartAngle = (180.0f - mSweepAngle) / 2;
private List mTabItems = new ArrayList<>();
private int mSelectTabIndex = -1;
private Paint mTabBackColorPaint;
private Paint mTabPaint;
private Paint mTabTtileTextPaint;//滑竿或點擊標題
private Paint mTabWheelPaint;
private Paint mTabTextPaint;
private Paint mTabPointerPaint;
private float mWheelCenterX;
private float mWheelCenterY;
private float mWheelRadius;
private RectF mWheelArcRect;
private float mPointerAngle;
private float mPointerRadius;
private boolean mIsMovingPointer = false;
private OnTabChangeListener mTabChangeListener = null;
private List mTabRectItem = new ArrayList();
private Paint textPaint = new Paint();
private float mMinRectRadius;//文字區域,
private float mMaxRectRadius;//文字區域,
public IndicatorTabView(Context context) {
this(context, null);
}
public IndicatorTabView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public IndicatorTabView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
mTabBackColorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//繪制最大的裡邊圖層背景
mTabBackColorPaint.setStyle(Paint.Style.FILL);
mTabBackColorPaint.setColor(Color.argb(255, 35, 47, 62));
mTabPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//繪制裡面最小圖層背景。
mTabPaint.setStyle(Paint.Style.FILL);
mTabPaint.setColor(Color.argb(255, 253,253, 254));
mTabTtileTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//繪制裡面最小圖層背景。
mTabTtileTextPaint.setStyle(Paint.Style.FILL);
mTabTtileTextPaint.setColor(Color.WHITE);
mTabWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTabWheelPaint.setStyle(Paint.Style.STROKE);
mTabWheelPaint.setStrokeWidth(getResources().getDimension(R.dimen.tab_wheel_width));
mTabWheelPaint.setColor(Color.argb(200, 253, 250, 245));
mTabWheelPaint.setStrokeCap(Paint.Cap.ROUND);//這個是設置繪制弧是,兩端圓滑;
mTabTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//繪制文字
mTabTextPaint.setColor(Color.argb(255, 253, 253, 254));
mTabTextPaint.setTextSize(getResources().getDimension(R.dimen.tab_text));
mTabTextPaint.setStrokeWidth(5);
mTabPointerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//繪制小點
mTabPointerPaint.setStyle(Paint.Style.FILL);
mTabPointerPaint.setColor(Color.argb(255, 255, 255, 255));
mPointerRadius = getResources().getDimension(R.dimen.tab_pointer_radius);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//繪制深藍背景。
canvas.drawCircle(mWheelCenterX, mWheelCenterY - getResources().getDimension(R.dimen.tab_wheel_padding_inner), mWheelRadius*1.2f, mTabBackColorPaint);
//繪制裡面第一條弧
canvas.drawCircle(mWheelCenterX, mWheelCenterY - getResources().getDimension(R.dimen.tab_wheel_padding_inner), mWheelRadius, mTabPaint);
canvas.drawArc(mWheelArcRect, mStartAngle, mSweepAngle, false, mTabWheelPaint);//繪制滑桿
for (int i = 0; i < mTabItems.size(); i++) {
IndicatorTabItem tempItem = mTabItems.get(i);
float angle = (tempItem.getStartAngle() + tempItem.getEndAngle()) / 2 - 90.0f;
canvas.save();//將之前繪制圖片保存起來,
canvas.rotate(angle, mWheelCenterX, mWheelCenterY);
canvas.drawText(tempItem.getName(), mWheelCenterX - tempItem.getMesureWidth() / 2,
getResources().getDimension(R.dimen.tab_wheel_width) + mWheelCenterY + mWheelRadius + getResources().getDimension(R.dimen.tab_wheel_padding_inner),
mTabTextPaint);
/***********************************************************************************************************/
Log.d(TAG, "-----------onDraw()-----------" + "X===[" + (mWheelCenterX - tempItem.getMesureWidth() / 2) + "]-------Y==={" + (getResources().getDimension(R.dimen.tab_wheel_width) + mWheelCenterY + mWheelRadius + getResources().getDimension(R.dimen.tab_wheel_padding_inner)) + "}");
float rectWidth = mTabTextPaint.measureText(tempItem.getName());
Paint.FontMetrics fm = mTabTextPaint.getFontMetrics();
float offsetAscent = fm.ascent;
float offsetBottom = fm.bottom;
float startX = mWheelCenterX - tempItem.getMesureWidth() / 2;
float startY = getResources().getDimension(R.dimen.tab_wheel_width) + mWheelCenterY + mWheelRadius + getResources().getDimension(R.dimen.tab_wheel_padding_inner);
Log.d(TAG, "TEXT-offsetAscent=" + offsetAscent);
// RectF testRect = new RectF(
// startX,
// (float) (startY + offsetAscent),
// (float) (startX + rectWidth),
// (float) (startY + offsetBottom)
// );
// textPaint.setColor(Color.argb(100, 233, 233, 0));
// canvas.drawRect(testRect, textPaint);
/*************************************************************************************************************************/
canvas.restore();//他的作用為,將之前的繪制保存的圖片save(),進行合並.
mMinRectRadius = distance(mWheelCenterX, startY + offsetAscent);//計算
mMaxRectRadius = distance(mWheelCenterX, startY + offsetBottom);
}
float[] pointerPosition = calculatePointerPosition(mPointerAngle);
canvas.drawCircle(mWheelCenterX + pointerPosition[0], mWheelCenterY + pointerPosition[1], mPointerRadius, mTabPointerPaint);//繪制小球
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
mWheelRadius = (float) (widthSize / (2.0f * Math.sin(Math.toRadians(32))));
int offset = (int) (heightSize - getResources().getDimension(R.dimen.tab_wheel_padding_bottom));
mWheelCenterY = offset - (int) Math.sqrt(Math.pow((double) mWheelRadius, 2) - Math.pow((double) (widthSize / 2), 2));
mWheelCenterX = widthSize / 2.0f;
mWheelArcRect = new RectF(mWheelCenterX - mWheelRadius, mWheelCenterY - mWheelRadius,
mWheelCenterX + mWheelRadius, mWheelCenterY + mWheelRadius);
}
private float[] calculatePointerPosition(float angle) {
float x = (float) (mWheelRadius * Math.cos(Math.toRadians(angle)));
float y = (float) (mWheelRadius * Math.sin(Math.toRadians(angle)));
return new float[]{x, y};
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX() - mWheelCenterX;
float y = event.getY() - mWheelCenterY;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float[] pointerPosition = calculatePointerPosition(mPointerAngle);
if (x >= (pointerPosition[0] - mPointerRadius * 2)
&& x <= (pointerPosition[0] + mPointerRadius * 2)
&& y >= (pointerPosition[1] - mPointerRadius * 2)
&& y <= (pointerPosition[1] + mPointerRadius * 2)) {
mIsMovingPointer = true;
return true;
}
float pointerLength = distanceRelative(x, y);//計算觸摸點距離圓心的坐標:
//計算文本觸摸區域的頂部距離圓心的坐標的距離:
//計算文本觸摸區域的底部距離圓心的坐標的距離;
//計算觸摸點的角度,
float tempPointerRectFAngle = (float) Math.toDegrees(Math.atan2(y, x));//文本觸摸區域的的角度
if (pointerLength >= mMinRectRadius - mPointerRadius && pointerLength <= mMaxRectRadius + mPointerRadius) {
int willSelectedIndex = -1;
for (int i = 0; i < mTabRectItem.size(); i++) {
IndicatorTabRectItem item = mTabRectItem.get(i);
if (tempPointerRectFAngle >= item.getStartAngle() && tempPointerRectFAngle <= item.getEndAngle()) {
willSelectedIndex = i;
break;
}
}
if (mSelectTabIndex != willSelectedIndex) {
if (mTabChangeListener != null) {
mTabChangeListener.onTabSelected(this, willSelectedIndex);
}
}
setSelection(willSelectedIndex);
invalidate();
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if (mIsMovingPointer) {
float tempPointerAngle = (float) Math.toDegrees(Math.atan2(y, x));
if (tempPointerAngle >= mTabItems.get(0).getStartAngle()
&& tempPointerAngle <= mTabItems.get(mTabItems.size() - 1).getEndAngle()) {
mPointerAngle = tempPointerAngle;
invalidate();
}
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mIsMovingPointer) {
mIsMovingPointer = false;
smoothMove();
return true;
}
break;
}
return false;
}
/**
* 根據觸摸點距離圓心的距離,與每一塊的活動角度,確定惟一的文本觸摸塊。
*/
// private void calculateTouchRect(float x, float y) {
//// =x-mWheelCenterX;
// float dpow2 = (float) (Math.pow((x - mWheelCenterX), 2) + Math.pow((y - mWheelCenterY), 2));
// float d = (float) Math.sqrt(dpow2);
// Log.d(TAG, "TouchRect---d=" + d);
// Log.d(TAG, "mMinRectRadius====" + mMinRectRadius);
// Log.d(TAG, "mMAXRectRadius====" + mMaxRectRadius);
//
//
// }
/**
* 相對與(x-mWheelCenterX,y-mWheelCenterY)坐標,
* 計算觸摸點。求兩點間距離,此時的圓心為
*/
public float distanceRelative(float x, float y) {
float dx = Math.abs(x);
float dy = Math.abs(y);
Log.d(TAG, "distance---d=" + (float) Math.hypot(dx, dy));
Log.d(TAG, "mMinRectRadius====" + mMinRectRadius);
Log.d(TAG, "mMAXRectRadius====" + mMaxRectRadius);
return (float) Math.hypot(dx, dy);
}
/**
* 計算觸摸點。求兩點間距離
*/
public float distance(float x, float y) {
float dx = Math.abs(x - mWheelCenterX);
float dy = Math.abs(y - mWheelCenterY);
Log.d(TAG, "distance---d=" + (float) Math.hypot(dx, dy));
Log.d(TAG, "mMinRectRadius====" + mMinRectRadius);
Log.d(TAG, "mMAXRectRadius====" + mMaxRectRadius);
return (float) Math.hypot(dx, dy);
}
private void smoothMove() {
int willSelectedIndex = -1;
for (int i = 0; i < mTabItems.size(); i++) {
IndicatorTabItem item = mTabItems.get(i);
if (mPointerAngle >= item.getStartAngle() && mPointerAngle <= item.getEndAngle()) {
willSelectedIndex = i;
break;
}
}
if (mSelectTabIndex != willSelectedIndex) {
if (mTabChangeListener != null) {
mTabChangeListener.onTabSelected(this, willSelectedIndex);
}
}
setSelection(willSelectedIndex);
}
public void setTabInfo(List tabInfo) {
if (tabInfo == null && (tabInfo.size() == 0 && tabInfo.size() > 4)) {
return;
}
float totalPercent = 0.0f;
for (int i = 0; i < tabInfo.size(); i++) {
IndicatorTabItem item = new IndicatorTabItem();
item.setName(tabInfo.get(i));
item.setMesureWidth(mTabTextPaint.measureText(item.getName()));
Log.d(TAG, "--------setTabInfo()-------" + item.getName());
totalPercent += item.getMesureWidth();
mTabItems.add(item);
}
float startAngle = mStartAngle;
for (int i = 0; i < mTabItems.size(); i++) {
IndicatorTabItem tempItem = mTabItems.get(i);
float itemSweepAngle = mSweepAngle * tempItem.getMesureWidth() / totalPercent;
tempItem.setStartAngle(startAngle);
tempItem.setEndAngle(startAngle + itemSweepAngle);
startAngle += itemSweepAngle;
Log.d(TAG, "startAngle" + i + "======" + startAngle);
Log.d(TAG, "EndAngle" + i + "======" + (startAngle + itemSweepAngle));
}
setSelection(0);
initIndicatorTabRectItem(tabInfo);
}
private void initIndicatorTabRectItem(List tabInfo) {
if (tabInfo == null && (tabInfo.size() == 0 && tabInfo.size() > 4)) {
return;
}
float totalPercent = 0.0f;
for (int i = 0; i < tabInfo.size(); i++) {
IndicatorTabRectItem rectItem = new IndicatorTabRectItem();
rectItem.setRectName(tabInfo.get(i));
rectItem.setRectWidth(mTabTextPaint.measureText(rectItem.getRectName()));
totalPercent += rectItem.getRectWidth();
Log.d(TAG, "----initIndicatorTabRectItem--------setRectWidth" + i + "======" + mTabTextPaint.measureText(rectItem.getRectName()));
mTabRectItem.add(rectItem);
}
float startAngle = mStartAngle;
for (int i = 0; i < mTabRectItem.size(); i++) {
IndicatorTabRectItem tempItem = mTabRectItem.get(i);
float itemSweepAngle = mSweepAngle * tempItem.getRectWidth() / totalPercent;
tempItem.setStartAngle(startAngle);
tempItem.setEndAngle(startAngle + itemSweepAngle);
startAngle += itemSweepAngle;
Log.d(TAG, "----initIndicatorTabRectItem-------startAngle" + i + "======" + startAngle);
Log.d(TAG, "----initIndicatorTabRectItem-------EndAngle" + i + "======" + (startAngle + itemSweepAngle));
}
}
/**
* 設置選擇小點的每一段中心點角度。
*
* @param index
*/
public void setSelection(int index) {
if (index < 0 || index > mTabItems.size() - 1) {
return;
}
mSelectTabIndex = index;
mPointerAngle = (mTabItems.get(mSelectTabIndex).getStartAngle() + mTabItems.get(mSelectTabIndex).getEndAngle()) / 2;
invalidate();
}
public void setTabChangeListener(OnTabChangeListener tabChangeListener) {
this.mTabChangeListener = tabChangeListener;
}
public interface OnTabChangeListener {
void onTabSelected(View v, int position);
}
}
3)IndicatorTabRectItem
package com.example.chenkui.myapplication;
/**
* Created by chenkui on 2016/10/11.
*/
public class IndicatorTabRectItem {
private float startX;
private float endX;
private float startY;
private float endY;
private float rectWidth;
private float rectHeigth;
private String rectName;
private float startAngle;
private float endAngle;
public float getStartX() {
return startX;
}
public void setStartX(float startX) {
this.startX = startX;
}
public float getEndX() {
return endX;
}
public void setEndX(float endX) {
this.endX = endX;
}
public float getStartY() {
return startY;
}
public void setStartY(float startY) {
this.startY = startY;
}
public float getEndY() {
return endY;
}
public void setEndY(float endY) {
this.endY = endY;
}
public float getRectWidth() {
return rectWidth;
}
public void setRectWidth(float rectWidth) {
this.rectWidth = rectWidth;
}
public float getRectHeigth() {
return rectHeigth;
}
public void setRectHeigth(float rectHeigth) {
this.rectHeigth = rectHeigth;
}
public String getRectName() {
return rectName;
}
public void setRectName(String rectName) {
this.rectName = rectName;
}
public float getStartAngle() {
return startAngle;
}
public void setStartAngle(float startAngle) {
this.startAngle = startAngle;
}
public float getEndAngle() {
return endAngle;
}
public void setEndAngle(float endAngle) {
this.endAngle = endAngle;
}
}
4)IndicatorTabItem
package com.example.chenkui.myapplication;
public class IndicatorTabItem {
private String name;
private float mesureWidth;
private float startAngle;
private float endAngle;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getMesureWidth() {
return mesureWidth;
}
public void setMesureWidth(float mesureWidth) {
this.mesureWidth = mesureWidth;
}
public float getStartAngle() {
return startAngle;
}
public void setStartAngle(float startAngle) {
this.startAngle = startAngle;
}
public float getEndAngle() {
return endAngle;
}
public void setEndAngle(float endAngle) {
this.endAngle = endAngle;
}
}
5)activity_main.xml
dimen.xml
16dp 10dp 130dp 30dp 20dp 10dp
效果圖如下:

Android 中使用代碼動態布局
Android 中使用代碼動態布局 本文介紹在android中使用代碼動態布局,有時候根據不同的需求,比如需要根據服務器上的條目個數來決定app中頁面布局控件(
手機qq設備鎖怎麼用 手機qq設備鎖設置教程
怎麼預防QQ號被盜,怎麼樣讓QQ號碼更加安全,手機qq設備鎖怎麼用?下面小編給大家帶來手機qq設備鎖設置教程,手機QQ4.62或以上版本才有此功能,一起來看
下拉列表,日期選擇器,時間選擇器,單項選擇,多項選擇
1.下拉列表Spinner 1.1.activity_main.xml Spinner是下拉列表的
用IntelliJ實現android gradle插件
android的gradle插件用了不少了,比如說官方的應用構建插件(com.android.application),lib構建插件(com.android.libra