Android實現ListView阻尼式(下拉回彈)效果
阿新 • • 發佈:2019-02-14
最近想模仿小米MIUI V5簡訊裡面的一個功能——私密簡訊,它的入口在簡訊列表,列表往下拉到1/3左右,我用Eclipse上的工具截了圖,包括該結構的佈局,如下圖:
從它的UI結構可以看出,用的是層疊結構,即將ListView和一個普通View(取名叫privateEntry)層疊在一起,ListView裡面有四個資料項,第0項和第3項都是FrameLayout,應該分別是新增的HeaderView(搜尋框)和FooterView,FooterView的用途應該是為了填充螢幕,擋住平時隱藏的那個普通View(取名叫privateEntry)。
下面是我自己寫的程式碼,借鑑了網上的BounceScrollView寫的BounceListView,實現後的效果沒有小米的那麼好,有待改進。
首先看BounceListView.java,這是主要實現程式碼:
再看main.xml的佈局,這個很簡單在FrameLayout中放置一個View和一個ListView:package com.lee.listviewdemo; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.GestureDetector.OnGestureListener; import android.view.animation.TranslateAnimation; import android.widget.ListView; public class BounceListView extends ListView { private boolean outBound = false; private int distance; private int firstOut; private Context mContext; private BounceCallBack mBounceCallback; public static final String TAG = "BounceListView"; private boolean isCalled = false; public BounceListView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; } public BounceListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mContext = context; } public BounceListView(Context context) { super(context); mContext = context; } GestureDetector gestureDetector = new GestureDetector(mContext, new OnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { // TODO Auto-generated method stub Log.d(TAG, "I'am onSingleTapUp()."); return false; } @Override public void onShowPress(MotionEvent e) { // TODO Auto-generated method stub Log.d(TAG, "I'am onShowPress()."); } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { Log.e(TAG, "Entry onscroll distanceX = " + distanceX + ", distanceY = " + distanceX); int firstPos = getFirstVisiblePosition(); // outbound Top if (outBound && firstPos != 0) { scrollTo(0, 0); return false; } View firstView = getChildAt(firstPos); // View lastView = getChildAt(lastPos - 1); if (!outBound) { firstOut = (int) e2.getRawY(); } if (firstView != null && (outBound || (firstPos == 0 && firstView.getTop() == 0 && distanceY < 0))) { // Record the length of each slide distance = firstOut - (int) e2.getRawY(); Log.e(TAG, "Lee--------distance = " + distance); Log.e(TAG, "firstOut = " + firstOut + ", firstPos = " + firstPos + ", firstView.getTop() = " + firstView.getTop()); int tempdistance = 60 * (-distance) / 100; //為了增加下拉的難度 if (tempdistance > getHeight() / 2) scrollTo(0, -getHeight() / 2); else scrollTo(0, -tempdistance); if (mBounceCallback != null && shouldCallBack(tempdistance)) { isCalled = true; mBounceCallback.onBounceCallBack(); } return true; } return false; } @Override public void onLongPress(MotionEvent e) { // TODO Auto-generated method stub Log.d(TAG, "I'am onLongPress()."); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // TODO Auto-generated method stub Log.d(TAG, "I'am onFling()."); return false; } @Override public boolean onDown(MotionEvent e) { // TODO Auto-generated method stub Log.d(TAG, "I'am onDown()."); return false; } }); private boolean shouldCallBack(int tempdistance) { if ((tempdistance > getHeight() / 2) && !isCalled) { //下拉到ListView高度的一半就會觸發事件 Log.e(TAG, "shouldCallBack()"); return true; } return false; } @Override public boolean dispatchTouchEvent(MotionEvent event) { int act = event.getAction(); if ((act == MotionEvent.ACTION_UP || act == MotionEvent.ACTION_CANCEL) && outBound) { outBound = false; isCalled = false; // scroll back } if (!gestureDetector.onTouchEvent(event)) { outBound = false; } else { outBound = true; } Rect rect = new Rect(); getLocalVisibleRect(rect); Log.i(TAG, "rect.top = " + rect.top); TranslateAnimation am = new TranslateAnimation(0, 0, -rect.top, 0); am.setDuration(300); startAnimation(am); scrollTo(0, 0); return super.dispatchTouchEvent(event); } public void setOnBounceCallBack(BounceCallBack callback) { mBounceCallback = callback; } //回撥觸發介面 public interface BounceCallBack { public void onBounceCallBack(); } }
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/LinearLayout01" android:layout_width="match_parent" android:layout_height="match_parent" > <View android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/privateEntry" android:background="@drawable/surprise" > </View> <com.lee.listviewdemo.BounceListView android:id="@+id/MyListView" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#00ffff" /> </FrameLayout>
然後是Main.java,
package com.lee.listviewdemo;
import java.util.ArrayList;
import java.util.HashMap;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.SimpleAdapter;
import android.util.Log;
import android.view.View;
import com.lee.listviewdemo.R;
import com.lee.listviewdemo.BounceListView.BounceCallBack;
public class Main extends Activity {
/** Called when the activity is first created. */
private View footerView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
BounceListView list = (BounceListView) findViewById(R.id.MyListView);
footerView = getLayoutInflater().inflate(R.layout.footer_container, null);
ArrayList<HashMap<String, String>> mylist = new ArrayList<HashMap<String, String>>();
for (int i = 0; i < 5; i++) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("ItemTitle", "BounceListView.....");
map.put("ItemText", "This is text.....");
mylist.add(map);
}
//設定listview彈性下拉事件觸發
list.setOnBounceCallBack(new BounceCallBack() {
@Override
public void onBounceCallBack() {
// TODO Auto-generated method stub
Intent intent = new Intent(Main.this,
TestActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
});
SimpleAdapter mSchedule = new SimpleAdapter(this,
mylist,
R.layout.my_listitem,
new String[] { "ItemTitle", "ItemText" },
new int[] { R.id.ItemTitle, R.id.ItemText });
//新增FooterView,當listview資料少時,可以填充螢幕
list.addFooterView(footerView, null, false);
list.setAdapter(mSchedule);
Log.e(BounceListView.TAG, "footerview == " + footerView + "list.count == " + list.getCount());
}
}
FooterView的佈局,就是加一個layout,讓其寬高自適應:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/footer_container" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff" />
</FrameLayout>
上面就是主要的實現程式碼了,接下來看一下效果圖:
弱弱的問一下我上傳的gif圖片不動,這是為啥呢。。以前沒上傳過
公司程式碼加了密,原始碼就不上傳了。。。
Android學習中。。。