編輯:關於Android編程
我K,今天居然是情人節,對於資深的單身狗來說,簡直是個噩耗,今天注定是各種秀恩愛,心塞中。。。。
話題到此結束,管他什麼情人節,今天給大家帶來的是一個浮層的上下滑動,浮層滑動時分三種狀態:全部顯示、顯
示一半、隱藏。可在浮層中添加ListView,GirdView,ImageView等等View。
具體的效果看下面的GIF圖:
1、在上面的浮層中我們可以看到存放著一個ListView,並能進行上下滾動,也就是說浮層的Touch事件需要在適
當的時候進行攔截,不傳遞給子View。這時需要重寫onInterceptTouchEvent()和onTouch()方法。
onInterceptTouchEvent和onTouch的介紹請參看下面:
1、onInterceptTouchEvent()是用於處理事件(類似於預處理,當然也可以不處理)並改變事件的傳遞方向,也就是
決定是否允許Touch事件繼續向下(子控件)傳遞,一但返回True(代表事件在當前的viewGroup中會被處理),則向
下傳遞之路被截斷(所有子控件將沒有機會參與Touch事件),同時把事件傳遞給當前的控件的onTouchEvent()處
理;返回false,則把事件交給子控件的onInterceptTouchEvent()。
2、onTouchEvent()用於處理事件,返回值決定當前控件是否消費(consume)了這個事件,也就是說在當前控件在處
理完Touch事件後,是否還允許Touch事件繼續向上(父控件)傳遞,一但返回True,則父控件不用操心自己來處理
Touch事件。返回true,則向上傳遞給父控件(注:可能你會覺得是否消費了有關系嗎,反正我已經針對事件編寫了
處理代碼?答案是有區別!比如ACTION_MOVE或者ACTION_UP發生的前提是一定曾經發生了ACTION_DOWN,如果你沒有
消費ACTION_DOWN,那麼系統會認為ACTION_DOWN沒有發生過,所以ACTION_MOVE或者ACTION_UP就不能被捕獲。)
2、在上圖中可以看出,當進行上下滾動時具有滾動效果,可以通過以下代碼實現:
ObjectAnimator anim=ObjectAnimator.ofFloat(this, translationY, values);
實現的原理其實很簡單,我們所要做的就是在onInterceptTouchEvent和onTouch兩個事件處理的方法中進行處理。
onInterceptTouchEvent的任務是判斷何時攔截事件,交由onTouch處理,何時由子View進行事件處理。
根據上圖可以很清楚知道onInterceptTouchEvent所要做的工作。在onTuch的中最主要的操作是在獲取滑動時的
MotionEvent.ACTION_MOVE中進行動畫的處理。
到此為止只說講解了兩個事件處理方法該做寫什麼,在圖中可以看出,滑動時主要有三種狀態,分別是全部顯示
、顯示一半、隱藏。
全部顯示:當浮層全部顯示時。這時浮層視圖不應該繼續向上滾動,這時需要在onInterceptTouchEvent中將事
件傳遞給它的子View進行處理;當向下滑動時,需要進行向下移動的動畫處理,滑動一次,這時的浮層視圖應該顯示
一半。
顯示一半:當浮層顯示一半時。這時進行向上滑動時需要執行向上移動的動畫處理,向下滑動時也一樣,進行移
動的動畫處理,進行隱藏浮層。
隱藏:當浮層進行隱藏時。可以通過點擊相應的點擊事件,使浮層從底部向上移動,並移動一半。
以上是大體的思路,具體實現請參看下面的代碼。
public class FloatingLayerView extends LinearLayout implements OnTouchListener { /** * 視圖顯示類型。 */ private int type=ALL; /** * 浮層的高度。 */ private int floating_height; /** * 浮層的寬度 */ private int floating_width; /** * 滑動高度 */ private float move_height; /** * 是否向下滑動,交由onTouch事件處理。 */ private boolean isCanHide=false; /** * 是否進行動畫 */ private boolean isCanAnimation=false; /** * 觸發攔截觸摸事件時的坐標點。 * 按下: * interceptTouch_X:按下時的X坐標點。 * interceptTouch_Y:按下時的Y坐標點。 * 滑動: * interceptMove_X:滑動時的X坐標點。 * interceptMove_Y:滑動時的Y坐標點。 * 距離: * interceptTouch_Move_X:從按下到滑動之間的距離(橫向滑動) * interceptTouch_Move_Y:從按下到滑動之間的距離(縱向滑動) * 滑動距離: * moveLength:根據此值判斷是否進行了滑動。 */ private float interceptTouch_X; private float interceptTouch_Y; private float interceptMove_X; private float interceptMove_Y; private float interceptTouch_Move_X; private float interceptTouch_Move_Y; private int moveLength=10; /** * 觸發觸摸事件時的坐標點 * down_X:按下時的X坐標點。 * down_Y:按下時的Y坐標點。 * move_X:移動時的X坐標點。 * move_Y:移動時的Y坐標點。 * down_move_X:橫向滑動的距離。 * down_move_Y:縱向滑動的距離。 */ private float down_X; private float down_Y; private float move_X; private float move_Y; private float down_move_X; private float down_move_Y; /** * 定義三種浮層顯示類型 * 0:不顯示 1:顯示一半 2:全部顯示 */ private static final int NONE=0; private static final int HALF=1; private static final int ALL=2; public FloatingLayerView(Context context, AttributeSet attrs) { super(context, attrs); setOnTouchListener(this); } public FloatingLayerView(Context context) { super(context); } @Override public void onWindowFocusChanged(boolean hasWindowFocus) { if(hasWindowFocus){ floating_width=getWidth(); floating_height=getHeight(); /** * 每次滑動的距離是當前View寬度的三分之一。 */ move_height=floating_height/3; } super.onWindowFocusChanged(hasWindowFocus); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { /** * 當按下時獲取x,y的坐標點。 */ case MotionEvent.ACTION_DOWN: interceptTouch_X = ev.getX(); interceptTouch_Y = ev.getY(); isCanAnimation=true; break; /** * 當滑動時操作如下: * 1、獲取滑動距離 * 2、判斷向上滑動還是向下滑動 * 3、向上滑動時 * 4、向下滑動時,判斷當前顯示方式: * (1)、顯示一半時,交由onTouch事件處理。 * (2)、全部顯示時,是否向下滑動交由當前View的子View處理, * 是否交由onTouch事件處理。 */ case MotionEvent.ACTION_MOVE: interceptMove_X = ev.getX(); interceptMove_Y = ev.getY(); interceptTouch_Move_X = Math .abs(interceptTouch_X - interceptMove_X); interceptTouch_Move_Y = Math .abs(interceptTouch_Y - interceptMove_Y); /** * 向下滑動 */ if(interceptMove_Y>interceptTouch_Y&&interceptTouch_Move_Y>moveLength&&interceptTouch_Move_Y>interceptTouch_Move_X){ return isDounTransferOnTouch(); } /** * 向上滑動 */ if(interceptTouch_Y>interceptMove_Y&&interceptTouch_Move_Y>moveLength&&interceptTouch_Move_Y>interceptTouch_Move_X){ return isUpTransferOnTouch(); } break; case MotionEvent.ACTION_UP: break; default: break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: break; /** * 當滑動時動畫操作 */ case MotionEvent.ACTION_MOVE: down_X=interceptTouch_X; down_Y=interceptTouch_Y; move_X=event.getX(); move_Y=event.getY(); down_move_X=Math.abs(down_X-move_X); down_move_Y=Math.abs(down_Y-move_Y); /** * 向下滑動 */ if(move_Y>down_Y&&down_move_Y>moveLength&&getCanAnimation()){ downAnimationConfig(); } /** * 向上滑動 */ if(down_Y>move_Y&&down_move_Y>moveLength&&getCanAnimation()){ upAnimationConfig(); } /** * 執行完上面動畫處理後,停止執行動畫 */ setCanAnimation(false); break; case MotionEvent.ACTION_UP: break; default: break; } return true; } /** * 是否進行動畫處理 * @return true:處理 */ private boolean getCanAnimation(){ return isCanAnimation; } /** * 獲取當前視圖顯示類型 * @return */ private int getType(){ return type; } private void setType(int type){ this.type=type; } /** * 設置是否進行動畫處理 * @param canAnimation */ private void setCanAnimation(boolean canAnimation){ this.isCanAnimation=canAnimation; } /** * 向下滑動時的動畫處理 */ private void downAnimationConfig(){ switch (getType()) { case HALF://當視圖顯示一半時 half2None(); break; case ALL://當視圖全部顯示時 all2Half(); break; default: break; } } /** * 向上滑動時的動畫處理 */ private void upAnimationConfig(){ switch (getType()) { case HALF://當視圖顯示一半時 half2All(); break; case ALL://當視圖全部顯示時 /** * 當視圖已經完整顯示,再往 * 上滑動也就沒任何意義進行 * 動畫處理。 */ break; default: break; } } /** * 向下滑動時是否交由onTouch事件處理 * @return true:由onTouch事件處理,不傳遞給子View */ private boolean isDounTransferOnTouch(){ switch (type) { case NONE: break; case HALF: return true; case ALL: if(isCanHide){ return true; } break; default: break; } return false; } /** * 向上滑動時是否交由onTouch事件處理 * @return true:由onTouch事件處理,不傳遞給子View */ private boolean isUpTransferOnTouch(){ switch (type) { case NONE: break; case HALF: return true; case ALL: break; default: break; } return false; } /** * 當向下滑動時,當前視圖顯示一半,再往下滑動隱藏。 * type設置為NONE */ private void half2None(){ float[] values=new float[]{move_height,getHeight()}; startAnimation(values); setType(NONE); } /** * 當向下滑動時,當前視圖顯示完整,再往下滑動視圖顯示一半。 * type設置為HALF */ private void all2Half(){ float[] values=new float[]{0,move_height}; startAnimation(values); setType(HALF); } /** * 當向上滑動時,當前視圖顯示一半,再往上滑動,視圖顯示完整。 * type設置為ALL */ private void half2All(){ float[] values=new float[]{move_height,0}; startAnimation(values); setType(ALL); } /** * 執行動畫 * @param values */ private void startAnimation(float[] values){ AnimatorSet as=new AnimatorSet(); ObjectAnimator anim=ObjectAnimator.ofFloat(this, translationY, values); anim.setDuration(500); as.playTogether(anim); as.start(); } /** * 當前視圖顯示完整時的動畫處理 */ private void all2None(){ float[] values=new float[]{0,getHeight()}; startAnimation(values); setType(HALF); } /** * 隱藏浮層 */ public void beforeInput(){ switch (getType()) { case NONE: break; case HALF: half2None(); break; case ALL: all2None(); break; default: break; } } /** * 顯示浮層一半 */ public void none2Half(){ float[] values=new float[]{getHeight(),move_height}; startAnimation(values); setType(HALF); } /** * 顯示全部浮層 */ public void none2All(){ float[] values=new float[]{getHeight(),0}; startAnimation(values); setType(HALF); } /** * 是否進行動畫滾動 * @param canHide */ public void setCanHide(boolean canHide){ this.isCanHide=canHide; } }
在代碼中已經進行了很詳細的注釋,在代碼的最後暴露了幾個可調用的方法,可以通過這幾個方法實現我們的滑動效
果。
在Activity中的通過GridView的OnScrollListener監聽事件進行判斷何時進行動畫的滾動,何時停止,當然在FloatingLa
yerView可以加上想要加的View。例如以下在FloatingLayerView中添加了GirdView:
Activity的代碼如下:
public class MainActivity extends Activity implements OnClickListener { private Button btn_show; private Button btn_hide; private GridView gv_all; private TestAdapter testAdapter = new TestAdapter(); // 覆蓋層 private FloatingLayerView mFloatingLayerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); addListener(); } private void initView() { btn_show = (Button) findViewById(R.id.btn_show); btn_hide = (Button) findViewById(R.id.btn_hide); // 覆蓋層 mFloatingLayerView = (FloatingLayerView) findViewById(R.id.activity_shine_ll_cover); gv_all = (GridView) findViewById(R.id.activity_shine_gv_all); gv_all.setAdapter(testAdapter); } private void addListener() { btn_show.setOnClickListener(this); btn_hide.setOnClickListener(this); gv_all.setOnScrollListener(scrollListener); } @Override public void onClick(View v) { switch (v.getId()) { // 顯示浮層 case R.id.btn_show: mFloatingLayerView.none2Half(); break; // 隱藏浮層 case R.id.btn_hide: mFloatingLayerView.beforeInput(); break; } } /** 覆蓋層中GridView滑動監聽 */ private OnScrollListener scrollListener = new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (firstVisibleItem == 0) { mFloatingLayerView.setCanHide(true); } else { mFloatingLayerView.setCanHide(false); } } }; // =============測試====================== private int[] images = new int[] { R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher }; class TestAdapter extends BaseAdapter { @Override public int getCount() { return images.length; } @Override public Object getItem(int position) { return images[position]; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = LayoutInflater.from(MainActivity.this).inflate( R.layout.image, null); ImageView imagView = (ImageView) view.findViewById(R.id.iv_show); imagView.setBackgroundResource(images[position]); return view; } } }
TabActivity在API13之後被fragment替代了,所以不建議使用效果:點擊頭像標簽,進行切換。 代碼:https://github.com/ldb
可滑動的標簽頁是很多應用的用來做外面框架的,比如微信,微博等等,我這裡實現的效果是下面是主標簽頁,然後中間一個的標簽頁裡面又可以繼續左右滑動,等於是標簽頁內部再嵌套標簽頁
這個圖片異步加載並緩存的類已經被很多開發者所使用,是最常用的幾個開源庫之一,主流的應用,隨便反編譯幾個火的項目,都可以見到它的身影。可是有的人並不知道如何去使用這庫如何進
實際上字母索引表的效果,可以說在現在的眾多APP中使用的非常流行,比如支付寶,微信中的聯系人,還有購物,買票的APP中選擇全國城市,切換城市的時候,這時候的城市也就是按照