分類:
1:自繪控件:動畫,顯示加載,顯示圖表,沒有對外界交互
2:組合控件:recyclviewId的Item
3:繼承控件:對系統控件進行修改
4:事件類控件:對事件衝突的處理
5:容器類控件:流式佈局,百分比佈局這些容器類控件,可以對子控件進行重新擺放
我們用demo分析自定義控件;
如圖所示:
實現思路:上面的箭頭理解爲下圖的 “小車”
只放重要的代碼
private float[] pos =new float[2];
private float[] tan =new float[2];
private float mFloat =0;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(0,getHeight()/2,getWidth(),getHeight()/2,mPaint);//先畫一條橫線
canvas.drawLine(getWidth()/2,0,getWidth()/2,getHeight(),mPaint);//再畫一條豎線
//將圓心設置到中心
canvas.translate(getWidth()/2,getHeight()/2);
//再畫一個圓
mPath.addCircle(0,0,100,Path.Direction.CW);
canvas.drawPath(mPath,mPaint);//不通過canvans直接畫圓
mFloat += 0.01;
if (mFloat >= 1){ //0-1的進度)
mFloat = 0;
}
//然後讓圖片顯示到圓上
//1 最開始顯示到某個點上 這個點通過PathMeasure的方法來確定 比如在0.1這個點開始
PathMeasure pathMeasure =new PathMeasure(mPath,false);
//從哪個點開始=pos[] 表示的是表示當前點在畫布上的位置 tan[]表示的是(當前點pos與x軸切點的點的座標)
pathMeasure.getPosTan(pathMeasure.getLength()*mFloat,pos,tan);
//計算小車的旋轉的角度
double degree = Math.atan2(tan[1],tan[0]) * 180.0 /Math.PI;
matrix.reset();
//將圖片根據每次不同的角度 而進行旋轉
matrix.postRotate((float)degree,mBitmap.getWidth() /2,mBitmap.getHeight()/2);
//將圖片的繪製點中心與當前點重合,通過上面我們已經獲取到了當前點 所以我們將圖片移動到這個位置就好了啊
matrix.postTranslate(pos[0]-mBitmap.getWidth()/2,pos[1]-mBitmap.getHeight()/2);
canvas.drawBitmap(mBitmap,matrix,mPaint);
invalidate();
}
Math.atan2() 方法可返回從 x 軸到點 (x,y) 之間的角度。
2:通過手寫RecyclerView學習組合控件
RecyclerView的用法
RecyclerView的架構在生活中的體現
傳送帶機制:
RecyclerView:
1:RecyclerView VS 傳送帶 用到了哪些設計模式呢?
2:RecyclerView中的
第一屏加載
加載第一屏幕的時候,先把加載需求交給回收池,看回收池裏面有沒有這個item,如果沒有,將需求轉交給適配器的onCrateViewHodler方法返回一個佈局給這個view,然後將這個view添加到Recycview的容器中
第二屏幕
當用戶的手指滑動,第一個item被滑出屏幕之外,回收池會將這個item回收,存放在他裏面的緩存集合,
回收的同時,底部需要被item填充,他第一步回到回收池中去找,如果要被填充的佈局和回收池中的類型是一樣的,
那麼就可以進行復用,他會去之前的緩存集合中將這個item取出來,由於這個item和底下的數據類型不一致,回收池會將這個item交給適配器去刷新數據。如果沒有,就繼續第一屏的繪製過程
我們將移除過程叫做 移除策略
我們將底部的填充叫做填充策略
2:那麼回收池應該怎麼設計呢?
前景:回收池本身就是一種集合,既能夠存也能夠取-------存和取 是回收池策略必須實現的
3:手寫RecyclerView的代碼如何設計
代碼(需要重寫自定義哪些方法呢)
onLayout:因爲是容器,對子控件進行擺放
OnMeasure:對自身和自控件進行測量
onIntercptTouchEvent:RecyclerView只對滑動事件進行消費,點擊事件是不需消費的,需要交給子item去消費
onTouchEvent:當消費了滑動是事件後,會將子控件進行重新擺放
Recycler 回收池使用的是Stack數組:
關於Stack :http://baijiahao.baidu.com/s?id=1640447389353256524&wfr=spider&for=pc
import android.view.View;
import java.util.Collection;
import java.util.Stack;
public class Recycler {
//如何查找最快 這次是要用棧
private Stack<View>[] views ;
public Recycler(int typeNumber) {
views = new Stack[typeNumber];
for (int i=0;i<typeNumber;i++) {
views[i] = new Stack<View>();
}
}
public void put(View view, int type){
views[type].push(view);
}
public View get(int type) {
try {
return views[type].pop();
} catch (Exception e) {
return null;
}
}
}
srcollyBy:是對整個擺放的實現邏輯
RecyclerView成員變量聲明:
這次只實現簡單功能
上代碼Demo;
public class RecyclerView extends ViewGroup {
private Adapter adapter;
//當前顯示的View
private List<View> viewList;
//當前滑動的y值
private int currentY;
//行數
private int rowCount;
//view的第一行 是佔內容的幾行
private int firstRow;
//y偏移量
private int scrollY;
//初始化 第一屏最慢
private boolean needRelayout;
private int width;
private int height;
private int[] heights;//item 高度
Recycler recycler;
//最小滑動距離
private int touchSlop;
public Adapter getAdapter() {
return adapter;
}
public void setAdapter(Adapter adapter) {
this.adapter = adapter;
if (adapter != null) {
recycler = new Recycler(adapter.getViewTypeCount());
scrollY = 0;
firstRow = 0;
needRelayout = true;
requestLayout();//1 onMeasure 2 onLayout
}
}
public RecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
ViewConfiguration configuration = ViewConfiguration.get(context);
this.touchSlop=configuration.getScaledTouchSlop();
this.viewList = new ArrayList<>();
this.needRelayout = true;
}
//初始化
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (needRelayout || changed) {
needRelayout = false;
viewList.clear();
removeAllViews();
if (adapter != null) {
// 擺放
width = r - l;
height = b - t;
int left, top = 0, right, bottom;
for (int i = 0; i < rowCount&&top<height; i++) {
right = width;
bottom = top + heights[i];
// 生成一個View
View view= makeAndStep(i, 0, top, width, bottom);
viewList.add(view);
top = bottom;//循環擺放
}
}
}
}
private View makeAndStep(int row, int left, int top, int right, int bottom) {
View view = obtainView(row, right - left, bottom - top);
view.layout(left, top, right, bottom);
return view;
}
private View obtainView(int row, int width, int height) {
// key type
int itemType= adapter.getItemViewType(row);
// 取不到
View reclyView = recycler.get(itemType);
View view = null;
if (reclyView == null) {
view = adapter.onCreateViewHodler(row, reclyView, this );
if (view == null) {
throw new RuntimeException("onCreateViewHodler 必須填充佈局");
}
}else {
view = adapter.onBinderViewHodler(row, reclyView, this);
}
view.setTag(R.id.tag_type_view, itemType);
view.measure(MeasureSpec.makeMeasureSpec(width,MeasureSpec.EXACTLY)
,MeasureSpec.makeMeasureSpec(height,MeasureSpec.EXACTLY));
addView(view,0 );
return view;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int h = 0;
if (adapter != null) {
this.rowCount = adapter.getCount();
heights = new int[rowCount];
for (int i = 0; i < heights.length; i++) {
heights[i] = adapter.getHeight(i);
}
}
// 數據的高度
int tmpH = sumArray(heights, 0, heights.length);
h= Math.min(heightSize, tmpH);
setMeasuredDimension(widthSize, h);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
// firstIndex firstIndex+count
private int sumArray(int array[], int firstIndex, int count) {
int sum = 0;
count += firstIndex;
for (int i = firstIndex; i < count; i++) {
sum += array[i];
}
return sum;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercept = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
currentY = (int) event.getRawY();
break;
}
case MotionEvent.ACTION_MOVE: {
int y2 = Math.abs(currentY - (int) event.getRawY());
if (y2 > touchSlop) {
intercept = true;
}
}
}
return intercept;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE: {
// 移動的距離 y方向
int y2 = (int) event.getRawY();
// // 上滑正 下滑負
int diffY = currentY - y2;
// 畫布移動 並不影響子控件的位置
scrollBy(0, diffY);
}
}
return super.onTouchEvent(event);
}
private int scrollBounds(int scrollY) {
// 上滑
if (scrollY > 0) {
scrollY = Math.min(scrollY,sumArray(heights,firstRow,heights.length-firstRow)-height);
}else {
// 極限值 會取零 非極限值的情況下 socrlly
scrollY = Math.max(scrollY, -sumArray(heights, 0, firstRow));
}
return scrollY;
// 下滑
}
@Override
public void scrollBy(int x, int y) {
// scrollY表示 第一個可見Item的左上頂點 距離屏幕的左上頂點的距離
scrollY += y;
scrollY = scrollBounds(scrollY);
// scrolly
if (scrollY > 0) {
// 上滑正 下滑負 邊界值
while (scrollY > heights[firstRow]) {
// 1 上滑移除 2 上劃加載 3下滑移除 4 下滑加載
removeView(viewList.remove(0));
scrollY -= heights[firstRow];
firstRow++;
}
while (getFillHeight() < height) {
int addLast = firstRow + viewList.size();
View view= obtainView(addLast, width, heights[addLast]);
viewList.add(viewList.size(), view);
}
} else if (scrollY < 0) {
// 4 下滑加載
while (scrollY < 0) {
int firstAddRow = firstRow - 1;
View view = obtainView(firstAddRow, width, heights[firstAddRow]);
viewList.add(0,view);
firstRow--;
scrollY += heights[firstRow+1];
}
// 3下滑移除
while (sumArray(heights, firstRow, viewList.size()) - scrollY - heights[firstRow + viewList.size() - 1] >= height) {
removeView(viewList.remove(viewList.size() - 1));
}
}else {
}
repositionViews();
}
private void repositionViews() {
int left, top, right, bottom, i;
top = - scrollY;
i = firstRow;
for (View view : viewList) {
bottom = top + heights[i++];
view.layout(0, top, width, bottom);
top = bottom;
}
}
private int getFillHeight() {
// 數據的高度 -scrollY
return sumArray(heights, firstRow, viewList.size()) - scrollY;
}
private int getFilledHeight() {
// 數據高度-scrolly
return sumArray(heights, firstRow, viewList.size()) - scrollY;
}
@Override
public void removeView(View view) {
super.removeView(view);
int key= (int) view.getTag(R.id.tag_type_view);
recycler.put(view, key);
}
interface Adapter {
View onCreateViewHodler(int position, View convertView, ViewGroup parent);
View onBinderViewHodler(int position, View convertView, ViewGroup parent);
//Item的類型
int getItemViewType(int row);
//Item的類型數量
int getViewTypeCount();
int getCount();
public int getHeight(int index);
}
}