編輯:關於Android編程
沒有使用第三方類庫,純代碼定制.主要用到的知識如下,
我們知道,不管是自定義View還是系統提供的TextView這些,它們都必須放置在LinearLayout等一些ViewGroup中,因此理論上我們可以很好的理解onMeasure(),onLayout(),onDraw()這三個函數:1.View本身大小多少,這由onMeasure()決定;2.View在ViewGroup中的位置如何,這由onLayout()決定;3.繪制View,onDraw()定義了如何繪制這個View。
一個MeasureSpec封裝了父布局傳遞給子布局的布局要求,每個MeasureSpec代表了一組寬度和高度的要求。一個MeasureSpec由大小和模式組成。它有三種模式:UNSPECIFIED(未指定),父元素部隊自元素施加任何束縛,子元素可以得到任意想要的大小;EXACTLY(完全),父元素決定自元素的確切大小,子元素將被限定在給定的邊界裡而忽略它本身大小;AT_MOST(至多),子元素至多達到指定大小的值。
它常用的三個函數:
1.static int getMode(int measureSpec):根據提供的測量值(格式)提取模式(上述三個模式之一)
2.static int getSize(int measureSpec):根據提供的測量值(格式)提取大小值(這個大小也就是我們通常所說的大小)
3.static int makeMeasureSpec(int size,int mode):根據提供的大小值和模式創建一個測量值(格式)
public static final int ACTION_DOWN = 0; // 按下事件
public static final int ACTION_UP = 1; // 抬起事件
public static final int ACTION_MOVE = 2; // 手勢移動事件
public static final int ACTION_CANCEL = 3; // 取消
還有觸摸事件,滾動的實現等,代碼注釋很詳細
public class MainUI extends RelativeLayout {
private Context context;
private FrameLayout middleMenu,leftMenu,rightMenu;
//設置模版
private FrameLayout middlemask;
private Scroller scroller;
//屬於自定義view,這裡就需要用到這兩個構造函數
public MainUI(Context context){
super(context);
inintView(context);
}
public MainUI(Context context, AttributeSet attrs) {
super(context, attrs);
inintView(context);
}
//初始化各種變量
public void inintView(Context context){
this.context = context;
//然後我們需要三個界面布局,這裡用framelayout來承載
//設置滑動的是當前content和滑動樣式
scroller = new Scroller(context,new DecelerateInterpolator());
leftMenu = new FrameLayout(context);
middleMenu = new FrameLayout(context);
rightMenu = new FrameLayout(context);
middlemask = new FrameLayout(context);
//為了區分給每個部分設置顏色
leftMenu.setBackgroundColor(Color.RED);
middleMenu.setBackgroundColor(Color.BLUE);
rightMenu.setBackgroundColor(Color.GREEN);
middlemask.setBackgroundColor(Color.GRAY);
middlemask.setAlpha(0);
//添加到當前布局
addView(leftMenu);
addView(middleMenu);
addView(rightMenu);
addView(middlemask);
//接下來要測量寬度,好用來給這三個部分分別設置寬度,放在onmeasure中
}
/**這個方法決定view本身的大小
* 這裡的兩個參數分別是屏幕的寬和高
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//中間的寬度正好是整個屏幕
middleMenu.measure(widthMeasureSpec, heightMeasureSpec);
middlemask.measure(widthMeasureSpec, heightMeasureSpec);
//旁邊的則最多為屏幕的80%
int realWidth = MeasureSpec.getSize(widthMeasureSpec);
int tempWidthMeasure = MeasureSpec.makeMeasureSpec(
(int)(realWidth*0.8f),MeasureSpec.EXACTLY);
leftMenu.measure(tempWidthMeasure, heightMeasureSpec);
rightMenu.measure(tempWidthMeasure, heightMeasureSpec);
}
/**這個方法決定view在layout中的位置
*四個參數對應屏幕 左上右下
* @param changed
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//中間菜單是中間的屏幕
middleMenu.layout(l, t, r, b);
middlemask.layout(l, t, r, b);
//左邊的菜單則左邊界要擴充
leftMenu.layout(l - leftMenu.getMeasuredWidth(), t, r, b);
//右邊界左右都要設置
rightMenu.layout(
l + middleMenu.getMeasuredWidth(),
t,
l + middleMenu.getMeasuredWidth()
+ rightMenu.getMeasuredWidth(), b);
//接下來添加滑動事件
}
private boolean isTsetCompete;
/**
* 處理相應的觸摸事件
* @param ev
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!isTsetCompete){
getEventType(ev);
return true;
}
//如果是左右滑動
if (isleftrightmove){
switch(ev.getActionMasked()){
case MotionEvent.ACTION_MOVE:
//得到滑動距離
int currScrollX = getScrollX();
//得到移動距離
int dis_x = (int)(ev.getX() -point.x);
//他倆差值肯定在20之間
int expectX = -dis_x +currScrollX;
int finalx=0;
if (expectX<0){
//右滑距離
finalx = Math.max(expectX,-leftMenu.getMeasuredWidth());
}else{
//左滑距離
finalx = Math.min(expectX,rightMenu.getMeasuredWidth());
}
scrollTo(finalx,0);
point.x = (int) ev.getX();
break;
//下面判斷繼續滑動或者手指離開屏幕
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
//判斷如果滑動距離大於一半則自動滑動出來,否則滑動回去
currScrollX = getScrollX();
if (Math.abs(currScrollX) > leftMenu.getMeasuredWidth() >>1) {
if (currScrollX < 0) {
scroller.startScroll(currScrollX, 0, -leftMenu.getMeasuredWidth() - currScrollX, 0);
}else {
scroller.startScroll(currScrollX,0,leftMenu.getMeasuredWidth()-currScrollX,0);
}
}else{
scroller.startScroll(currScrollX,0,-currScrollX,0);
}
//用於屏幕刷新
invalidate();
isleftrightmove = false;
isTsetCompete = false;
break;
}
}
return super.dispatchTouchEvent(ev);
}
/**
* 在滑動改變距離的同時改變透明度,這種方式很好,因為會先調用父類的方法,所以不會影響到原來程序的運行
* @param x
* @param y
*/
@Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
int cruX = Math.abs(getScrollX());
float fo = cruX / (float)middleMenu.getMeasuredWidth();
middlemask.setAlpha(fo);
}
/**
* 滾動條的回調方法
*/
@Override
public void computeScroll() {
super.computeScroll();
if (!scroller.computeScrollOffset()){
return;
}
int tempX = scroller.getCurrX();
scrollTo(tempX,0);
}
private Point point = new Point();
private static final int TSET_DIS = 20;
private boolean isleftrightmove;
/**
* 用於判斷觸摸事件類型的函數
* @param ev
*/
private void getEventType(MotionEvent ev) {
switch(ev.getActionMasked()){
case MotionEvent.ACTION_DOWN:
//得到當前坐標
point.x = (int) ev.getX();
point.y = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int dX = Math.abs((int)ev.getX()-point.x);
int dY = Math.abs((int)ev.getY()-point.y);
//左右滑動
if (dX >=TSET_DIS && dX>dY){
isleftrightmove = true;
isTsetCompete = true;
//為了滑動後可以再次滑動
point.x = (int) ev.getX();
point.y = (int) ev.getY();
}else if (dY>=TSET_DIS&&dY>dX){
isleftrightmove = false;
isTsetCompete = false;
point.x = (int) ev.getX();
point.y = (int) ev.getY();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
}
}
Android 解決監聽home鍵的幾種方法前言:以下兩種方法可以完美解決監聽back鍵,home鍵,多任務鍵(最近任務鍵)。一、使用注冊廣播監聽home鍵、多任務鍵演示
Android Studio添加Parcelable序列化小工具(快速提高開發效率)Android Studio是google專門為開發Android提供的開發工具,在它
先來看看效果:一、添加依賴庫的步驟1.項目的gradle文件內的做以下改動allprojects { repositories { ... maven { ur
基本介紹現在的一些購物類App例如淘寶,京東等,在物品詳情頁,都采用了類似分層的模式,即上拉加載詳情的方式,節省了空間,使用戶的體驗更加的舒適。只要對於某個東西的介紹很多