編輯:關於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;
}
}
}
Android Tab -- 使用TabWidget、TabHost、TabActivity來實現 - liDB - 博客園
TabActivity在API13之後被fragment替代了,所以不建議使用效果:點擊頭像標簽,進行切換。 代碼:https://github.com/ldb
android開發步步為營之53:viewpager+fragment構造可左右滑動標簽頁效果
可滑動的標簽頁是很多應用的用來做外面框架的,比如微信,微博等等,我這裡實現的效果是下面是主標簽頁,然後中間一個的標簽頁裡面又可以繼續左右滑動,等於是標簽頁內部再嵌套標簽頁
Android-Universal-Image-Loader 圖片異步加載類庫的使用分析(超詳細配置)
這個圖片異步加載並緩存的類已經被很多開發者所使用,是最常用的幾個開源庫之一,主流的應用,隨便反編譯幾個火的項目,都可以見到它的身影。可是有的人並不知道如何去使用這庫如何進
淺談android中手機聯系人字母索引表的實現
實際上字母索引表的效果,可以說在現在的眾多APP中使用的非常流行,比如支付寶,微信中的聯系人,還有購物,買票的APP中選擇全國城市,切換城市的時候,這時候的城市也就是按照