枕上从妨一夜睡,灯前读尽十年诗。这篇文章主要讲述android 自定义View弯曲滑竿指示器相关的知识,希望能为你提供帮助。
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<
String>
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<
IndicatorTabItem>
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<
IndicatorTabRectItem>
mTabRectItem =
new ArrayList<
IndicatorTabRectItem>
();
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<
String>
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<
String>
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;
privateString 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
<
?xml version=
"
1.0"
encoding=
"
utf-8"
?>
<
RelativeLayout xmlns:android=
"
http://schemas.android.com/apk/res/android"
android:layout_width=
"
match_parent"
android:layout_height=
"
188dp"
android:orientation=
"
vertical"
>
<
com.example.chenkui.myapplication.IndicatorTabView
android:id=
"
@
+
id/myView"
android:layout_width=
"
match_parent"
android:layout_height=
"
match_parent"
android:layout_alignParentTop=
"
true"
android:layout_alignParentLeft=
"
true"
android:layout_alignParentStart=
"
true"
/>
<
ImageView
android:id=
"
@
+
id/titleTavIconImg"
android:layout_width=
"
80dp"
android:layout_height=
"
42dp"
android:layout_marginTop=
"
15dp"
android:layout_centerHorizontal=
"
true"
android:background=
"
@
drawable/title_icon"
/>
<
/RelativeLayout>
【android 自定义View弯曲滑竿指示器】dimen.xml
<
resources>
<
!-- Default screen margins, per the Android Design guidelines. -->
<
dimen name=
"
tab_text"
>
16dp<
/dimen>
<
dimen name=
"
tab_wheel_width"
>
10dp<
/dimen>
<
dimen name=
"
tab_wheel_padding_bottom"
>
130dp<
/dimen>
<
dimen name=
"
tab_wheel_padding_outer"
>
30dp<
/dimen>
<
dimen name=
"
tab_wheel_padding_inner"
>
20dp<
/dimen>
<
dimen name=
"
tab_pointer_radius"
>
10dp<
/dimen>
<
/resources>
效果图如下:
文章图片
github源码链接地址:
https://github.com/Andrewcenquck/MyApplication
推荐阅读
- Android自定义View之仿QQ侧滑菜单实现
- 学习笔记Android给数字添加背景
- Android开发学习——打电话应用
- 初识 Android
- Android开发学习——应用安装过程
- Android 微信小视频录制功能实现
- Selenium的组件详细介绍和指南
- C++ STL如何使用fill函数(代码示例)
- PHP如何使用getprotobynumber()函数(示例)