1.本文的姊妹篇:看得見的數據結構Android版之表的數組實現(數據結構篇)
2.但願你能夠和我在Github一同見證:DS4Android的誕生與成長,歡迎star
3.激動人心的時刻到了,要畫圖了,鉛筆、草稿紙、飲料、花生米準備好,如今開始了java
表結構的常規操做git
數組的擴容與縮容github
準備一個主畫筆和主路徑並肯定一些常量
而後用analyze
包繪製封裝好的網格和座標系以便查看編程
/**
* 做者:張風捷特烈<br/>
* 時間:2018/11/21 0021:8:01<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:數組實現表結構---測試視圖
*/
public class ArrayView<E> extends View {
private Point mCoo = new Point(200, 150);//座標系
private Picture mCooPicture;//座標系canvas元件
private Picture mGridPicture;//網格canvas元件
private Path mPath;//主路徑
private Paint mPaint;//主畫筆
private static final int OFFSET_X = 10;//X空隙
private static final int OFFSET_Y = 60;//Y空隙
private static final int OFFSET_OF_TXT_Y = 10;//文字的偏移
private static final int BOX_RADIUS = 10;//數組盒子的圓角
public ArrayView(Context context) {
this(context, null);
}
public ArrayView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();//初始化
}
private void init() {
//初始化主畫筆
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(5);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setTextSize(50);
//初始化主路徑
mPath = new Path();
//輔助線
mCooPicture = HelpDraw.getCoo(getContext(), mCoo,false);//座標系:無文字
mGridPicture = HelpDraw.getGrid(getContext());//網格
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
HelpDraw.draw(canvas, mGridPicture);
canvas.save();
canvas.translate(mCoo.x, mCoo.y);//畫布移到座標原點
//TODO draw
canvas.restore();
HelpDraw.draw(canvas, mCooPicture);
}
}
複製代碼
private void init() {
//初始化主畫筆
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(5);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setTextSize(50);
//初始化主路徑
mPath = new Path();
//初始化文字畫筆
mTxtPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTxtPaint.setColor(Color.WHITE);
mTxtPaint.setTextAlign(Paint.Align.CENTER);
mTxtPaint.setTextSize(40);
//初始化路徑畫筆
mPathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPathPaint.setColor(Color.GRAY);
mPathPaint.setStyle(Paint.Style.STROKE);
mCooPicture = HelpDraw.getCoo(getContext(), mCoo,false);
mGridPicture = HelpDraw.getGrid(getContext());
//初始化圓球按鈕畫筆
mCtrlPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCtrlPaint.setColor(Color.RED);
mCtrlPaint.setTextAlign(Paint.Align.CENTER);
mCtrlPaint.setTextSize(30);
}
複製代碼
你也能夠將這些信息封裝成一個bean,這裏爲了bean太多容易混淆,用幾個數組記錄信息canvas
private static final Point[] CTRL_POS = new Point[]{//控制按鈕的點位
new Point(-100, 100),//添加
new Point(-100, 300),//更新
new Point(-100, 500),//查看
new Point(-100, 700),//刪除
new Point(700, -70),//定點添加
new Point(700 + 300, -70),//定值查詢
new Point(700 + 300 * 2, -70),//定點刪除
new Point(700 + 300 * 3, -70),//清除
};
private static int[] CTRL_COLOR = new int[]{//控制按鈕的顏色
0xff1EF519,//添加
0xff2992F2,//更新
0xffB946F4,//添加
0xffF50C0C,//刪除
0xff1EF519,//定點添加
0xffB946F4,//定值查詢
0xffF50C0C,//定點刪除
0xffF46410,//清除
};
private static final String[] CTRL_TXT = new String[]{//控制按鈕的文字
"添加",//添加
"更新",//更新
"查尋",//添加
"刪除",//刪除
"定點+",//定點添加
"值查",//定值查詢
"定點-",//定點刪除
"清空",//清除按鍵
};
private static final int CTRL_RADIUS = 50;//控制按鈕的半徑
複製代碼
人狠話很少
----一個循環,一波帶走數組
/**
* 控制面板--圓球
*
* @param canvas 畫布
*/
private void ctrlView(Canvas canvas) {
for (int i = 0; i < CTRL_POS.length; i++) {
mCtrlPaint.setColor(CTRL_COLOR[i]);
canvas.drawCircle(CTRL_POS[i].x, CTRL_POS[i].y, CTRL_RADIUS, mCtrlPaint);
canvas.drawText(CTRL_TXT[i], CTRL_POS[i].x, CTRL_POS[i].y + OFFSET_OF_TXT_Y, mTxtPaint);
}
}
複製代碼
這就...畫完了?對...這就畫完了bash
/**
* 審斷之神能力一:第一形態:區域限定----判斷出是否在某點的半徑爲r圓範圍內
*
* @param srcX 目標點X
* @param srcY 目標點Y
* @param dstX 主動點X
* @param dstY 主動點Y
* @param r 半徑區域
* @return 是否在區域內
*/
public static boolean judgeCircleArea(float srcX, float srcY, float dstX, float dstY, float r) {
return disPos2d(srcX, srcY, dstX, dstY) <= r;
}
/**
* 審斷之神被動能力一:兩點間距離函數
* @param srcX 目標點X
* @param srcY 目標點Y
* @param dstX 主動點X
* @param dstY 主動點Y
* @return 兩點間距離函數
*/
private static float disPos2d(float srcX, float srcY, float dstX, float dstY) {
return (float) Math.sqrt((srcX - dstX) * (srcX - dstX) + (srcY - dstY) * (srcY - dstY));
}
複製代碼
OnCtrlClickListener
兵馬未動,糧草先行
,有接口好辦事微信
/**
* 做者:張風捷特烈<br/>
* 時間:2018/11/21 0021:10:17<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:控操做接口
*/
public interface OnCtrlClickListener<T> {
/**
* 添加時回調
* @param view
*/
void onAdd(T view);
/**
* 定點添加時回調
* @param view
*/
void onAddByIndex(T view);
/**
* 移除時回調
* @param view
*/
void onRemove(T view);
/**
* 定點移除時回調
* @param view
*/
void onRemoveByIndex(T view);
/**
* 設置時回調
* @param view
*/
void onSet(T view);
/**
* 查詢時回調
* @param view
*/
void onFind(T view);
/**
* 定值查詢時回調
* @param view
*/
void onFindByData(T view);
/**
* 清空時回調
* @param view
*/
void onClear(T view);
}
複製代碼
仍是一個for循環一波帶走----(mark:一開始我也是一個一個if,而後發現能夠優化,才變得優雅的)
也許沒法一步想到位,但能夠先實現,而後再優化,我就喜歡這種一波帶走的感受
注意點:downX和downY要相對於canvas的座標系,因此要偏移一下數據結構
private OnCtrlClickListener<ArrayView<E>> mOnCtrlClickListener;//監聽器
public void setOnCtrlClickListener(OnCtrlClickListener<ArrayView<E>> onCtrlClickListener) {
mOnCtrlClickListener = onCtrlClickListener;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float downX = event.getX() - mCoo.x;
float downY = event.getY() - mCoo.y;
for (int i = 0; i < CTRL_POS.length; i++) {
if (JudgeMan.judgeCircleArea( //區域斷定
CTRL_POS[i].x, CTRL_POS[i].y,
downX, downY, CTRL_RADIUS * 1.2f)) {
if (mOnCtrlClickListener != null) {
switch (i) {
case 0://插入尾部
mOnCtrlClickListener.onAdd(this);
break;
case 1://更新
mOnCtrlClickListener.onSet(this);
contactTest();
break;
case 2://查找
mOnCtrlClickListener.onFind(this);
break;
case 3://刪除尾部
selectIndex = mArrayBoxes.size() - 1;
mAnimator.start();
break;
case 4://定點添加尾部
mOnCtrlClickListener.onAddByIndex(this);
break;
case 5://定值查詢
mOnCtrlClickListener.onFindByData(this);
break;
case 6://定點移除
mAnimator.start();
break;
case 7://清空
mOnCtrlClickListener.onClear(this);
break;
}
CTRL_COLOR[i] = 0xff54E1F8;//點擊更換顏色
}
}
}
updayeSelectIndex(downX, downY);//更新selectIndex
break;
case MotionEvent.ACTION_UP://還原顏色
CTRL_COLOR[0] = 0xff1EF519;
CTRL_COLOR[1] = 0xff2992F2;
CTRL_COLOR[2] = 0xffB946F4;
CTRL_COLOR[3] = 0xffF50C0C;
CTRL_COLOR[4] = 0xff1EF519;
CTRL_COLOR[5] = 0xffB946F4;
CTRL_COLOR[6] = 0xffF50C0C;
CTRL_COLOR[7] = 0xffF46410;
break;
}
invalidate();//記得重繪
return true;
}
複製代碼
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayView<String> view = new ArrayView<>(this);
view.setOnCtrlClickListener(new OnCtrlClickListener<ArrayView<String>>() {
@Override
public void onAdd(ArrayView<String> view) {
Toast.makeText(MainActivity.this, "onAdd", Toast.LENGTH_SHORT).show();
}
@Override
public void onAddByIndex(ArrayView<String> view) {
Toast.makeText(MainActivity.this, "onAddByIndex", Toast.LENGTH_SHORT).show();
}
@Override
public void onRemove(ArrayView<String> view) {
Toast.makeText(MainActivity.this, "onRemove", Toast.LENGTH_SHORT).show();
}
@Override
public void onRemoveByIndex(ArrayView<String> view) {
Toast.makeText(MainActivity.this, "onRemoveByIndex", Toast.LENGTH_SHORT).show();
}
@Override
public void onSet(ArrayView<String> view) {
Toast.makeText(MainActivity.this, "onSet", Toast.LENGTH_SHORT).show();
}
@Override
public void onFind(ArrayView<String> view) {
Toast.makeText(MainActivity.this, "onFind", Toast.LENGTH_SHORT).show();
}
@Override
public void onFindByData(ArrayView<String> view) {
Toast.makeText(MainActivity.this, "onFindByData", Toast.LENGTH_SHORT).show();
}
@Override
public void onClear(ArrayView<String> view) {
Toast.makeText(MainActivity.this, "onFindByData", Toast.LENGTH_SHORT).show();
}
});
setContentView(view);
}
}
複製代碼
OK,完美運行,核心在審斷之神的
judgeCircleArea
dom
擁有座標、顏色、速度三種核心屬性
/**
* 做者:張風捷特烈<br/>
* 時間:2018/11/21 0021:8:50<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:可顯示出來的基本條件
*/
public class Viewable {
public float x;//單體的x座標
public float y;//單體的y座標
public int color = 0xff43A3FA;//單體的顏色
public float vX;//單體的水平速度
public float vY;//單體的數值速度
public Viewable() {
}
public Viewable(float x, float y) {
this.x = x;
this.y = y;
}
}
複製代碼
數組盒子:擁有索引和數據兩個額外屬性
/**
* 做者:張風捷特烈<br/>
* 時間:2018/11/21 0021:8:46<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:模型層數組的單體
*/
public class ArrayBox<T> extends Viewable {
public int index;//數組單體索引
public T data;//數據結構承載的核心數據
@Override
public boolean equals(Object obj) {
return ((ArrayBox) obj).data == data;
}
public ArrayBox(T data) {
this.data = data;
}
public ArrayBox(float x, float y) {
super(x, y);
}
}
複製代碼
就是稍微畫個圖,看看有什麼關係,找到通式就ok了(代碼巧多了,寫字就是醜...)
而後咱們用上篇的數組表結構
來進行測試
根據上面的分析圖,肯定了第x列,第y行的矩形座標,應該就不難畫了
注意:繪製數組的長度個空白矩形,數組的長度!! 數組的長度個!!,不是集合大小
原本應該把數組徹底封裝在數組表結構中的,這裏爲了演示擴容和縮容,數組長度仍是必要的
//建立一個上篇定義的數組表類,泛型固然是要畫的數組盒子了
private IChart<ArrayBox<E>> mArrayBoxes = new ArrayChart<>();
/**
* 繪製數組的長度個空白矩形
*
* @param canvas
*/
private void helpView(Canvas canvas) {
for (int i = 0; i < mArrayBoxes.capacity(); i++) {
int y = i / 8;//行座標
int x = i % 8;//列座標
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(0xff821AFA);
canvas.drawRoundRect(
(Cons.BOX_WIDTH + OFFSET_X) * x, (Cons.BOX_HEIGHT + OFFSET_Y) * y,
(Cons.BOX_WIDTH + OFFSET_X) * x + Cons.BOX_WIDTH,
(Cons.BOX_HEIGHT + OFFSET_Y) * y + Cons.BOX_HEIGHT,
BOX_RADIUS, BOX_RADIUS, mPaint);
mTxtPaint.setColor(0xff821AFA);
canvas.drawText(i + "",
(Cons.BOX_WIDTH + OFFSET_X) * x + Cons.BOX_WIDTH / 2,
(Cons.BOX_HEIGHT + OFFSET_Y) * y + 3 * OFFSET_OF_TXT_Y, mTxtPaint);
}
}
複製代碼
數組初始長度爲10,沒錯
根據mArrayBoxes的大小,繪製出mArrayBoxes裏面的全部元素。
繪製單體在前言篇已經給出,這裏只是加上了偏移量,應該不難理解。
/**
* 繪製表結構
* @param canvas
*/
private void dataView(Canvas canvas) {
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.FILL);
mPath.reset();
for (int i = 0; i < mArrayBoxes.size(); i++) {
ArrayBox box = mArrayBoxes.get(i);
mPaint.setColor(box.color);
canvas.drawRoundRect(
box.x, box.y, box.x + Cons.BOX_WIDTH, box.y + Cons.BOX_HEIGHT,
BOX_RADIUS, BOX_RADIUS, mPaint);
mPath.moveTo(box.x, box.y);
mPath.rCubicTo(Cons.BOX_WIDTH / 2, Cons.BOX_HEIGHT / 2,
Cons.BOX_WIDTH / 2, Cons.BOX_HEIGHT / 2, Cons.BOX_WIDTH, 0);
canvas.drawPath(mPath, mPathPaint);
canvas.drawText(box.data + "",
box.x + Cons.BOX_WIDTH / 2,
box.y + Cons.BOX_HEIGHT / 2 + 3 * OFFSET_OF_TXT_Y, mTxtPaint);
}
}
複製代碼
每次增刪操做後更新一下點位
/**
* 更新繪製單體的點位
*/
private void updatePosOfData() {
for (int i = 0; i < mArrayBoxes.size(); i++) {
int y = i / 8;//行座標
int x = i % 8;//列座標
ArrayBox box = mArrayBoxes.get(i);
box.x = (Cons.BOX_WIDTH + OFFSET_X) * x;
box.y = (Cons.BOX_HEIGHT + OFFSET_Y) * y;
box.vY = 100;
box.vX = 100;
}
}
複製代碼
/**
* 點擊時動態更新選中值
* @param downX 按下點X
* @param downY 按下點Y
*/
private void updateSelectIndex(float downX, float downY) {
float x = downX / (Cons.BOX_WIDTH + OFFSET_X) - 0.5f;
float y = downY / (Cons.BOX_HEIGHT + OFFSET_Y) - 0.5f;
if (x > -0.5 && y > -0.5) {
int indexOfData = Math.round(y) * 8 + Math.round(x);//逆向求取點中的數據索引
if (indexOfData < mArrayBoxes.size()) {
mArrayBoxes.get(indexOfData).color = ColUtils.randomRGB();
selectIndex = indexOfData;
}
}
}
複製代碼
注意:如下操做是在Activity中的點擊回調中進行,調用了view層的方法,實現操做與視圖分離
/**
* 視圖的數據操做接口方法--添加
*
* @param data 數據
*/
public void addData(E data) {
ArrayBox<E> arrayBox = new ArrayBox<>(0, 0);
arrayBox.data = data;
mArrayBoxes.add(arrayBox);
updatePosOfData();//更新一下點位
}
複製代碼
* 視圖的數據操做接口方法--根據索引添加
*
* @param index
* @param data
*/
public void addDataById(int index, E data) {
if (mArrayBoxes.size() > 0 && index < mArrayBoxes.size() && index >= 0) {
ArrayBox<E> arrayBox = new ArrayBox<>(0, 0);
arrayBox.data = data;
mArrayBoxes.add(index, arrayBox);
updatePosOfData();
}
}
複製代碼
/**
* 視圖的數據操做接口方法--根據id查詢操做
* @param index
* @return
*/
public E findData(int index) {
if (mArrayBoxes.size() > 0 && index < mArrayBoxes.size() && index >= 0) {
return mArrayBoxes.get(index).data;
}
return null;
}
/**
* 更新數據
* @param index 索引
* @param data 數據
*/
public void setData(int index, E data) {
if (mArrayBoxes.size() > 0 && index < mArrayBoxes.size() && index >= 0) {
mArrayBoxes.get(index).data = data;
}
}
複製代碼
/**
* 視圖的數據操做接口方法--根據數據查詢操做
* @param data
* @return
*/
public int[] findData(E data) {
ArrayBox<E> arrayBox = new ArrayBox<>(0, 0);
arrayBox.data = data;
return mArrayBoxes.getIndex(arrayBox);
}
複製代碼
/**
* 視圖的數據操做接口方法--移除末尾
*/
public void removeData() {
if (mArrayBoxes.size() > 0) {
mArrayBoxes.remove();
updatePosOfData();
}
}
/**
* 視圖的數據操做接口方法--定索引刪除操做
*
* @param index 索引
*/
public void removeData(int index) {
if (mArrayBoxes.size() > 0 && index < mArrayBoxes.size() && index >= 0) {
//更新後面的索引
for (int i = index; i < mArrayBoxes.size(); i++) {
mArrayBoxes.get(i).index -= 1;
}
mArrayBoxes.remove(index);
selectIndex = -1;
updatePosOfData();
}
}
複製代碼
//初始化時間流ValueAnimator
mAnimator = ValueAnimator.ofFloat(0, 1);
mAnimator.setRepeatCount(-1);
mAnimator.setDuration(2000);
mAnimator.setRepeatMode(ValueAnimator.REVERSE);
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.addUpdateListener(animation -> {
updateBall();//更新小球位置
invalidate();
});
複製代碼
/**
* 更新小球
*/
private void updateBall() {
if (mArrayBoxes.size() <= 0 && selectIndex < 0) {
return;
}
ArrayBox ball = mArrayBoxes.get(selectIndex);
ball.x += ball.vX;
ball.y += ball.vY;
if (ball.y > 600) {//大於600就執行移除
if (mOnCtrlClickListener != null) {
mOnCtrlClickListener.onRemoveByIndex(this);//移除監聽放在這裏了!!
mAnimator.pause();//暫停ValueAnimator
}
}
}
複製代碼
//點擊的監聽中修改:
case 3://刪除尾部---這裏將移除的選中對象設爲最後一個
selectIndex = mArrayBoxes.size() - 1;
mAnimator.start();//開啓Animator
複製代碼
private void contactTest() {
IChart<ArrayBox<E>> contactArr = new ArrayChart<>();
contactArr.add(new ArrayBox<E>((E) "toly1"));
contactArr.add(new ArrayBox<E>((E) "toly2"));
contactArr.add(new ArrayBox<E>((E) "toly3"));
contactData(selectIndex, contactArr);
}
public void contactData(int index, IChart<ArrayBox<E>> chart) {
mArrayBoxes.contact(index, chart);
updatePosOfData();
}
複製代碼
基本上就是這樣,思路什麼的都理清了,細節方面可參看源碼
項目源碼 | 日期 | 備註 |
---|---|---|
V0.1--github | 2018-11-23 | 看得見的數據結構Android版之表的數組實現(視圖篇) |
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
個人github | 個人簡書 | 個人掘金 | 我的網站 |
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大編程愛好者共同交流
3----我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正
4----看到這裏,我在此感謝你的喜歡與支持