最近想模仿小米MIUI V5短信里面的一个功能——私密短信,它的入口在短信列表,列表往下拉到1/3左右,我用Eclipse上的工具截了图,包括该结构的布局,如下图:
从它的UI结构可以看出,用的是层叠结构,即将ListView和一个普通View(取名叫privateEntry)层叠在一起,ListView里面有四个数据项,第0项和第3项都是FrameLayout,应该分别是添加的HeaderView(搜索框)和FooterView,FooterView的用途应该是为了填充屏幕,挡住平时隐藏的那个普通View(取名叫privateEntry)。
下面是我自己写的代码,借鉴了网上的BounceScrollView写的BounceListView,实现后的效果没有小米的那么好,有待改进。
首先看BounceListView.java,这是主要实现代码:
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(); } }再看main.xml的布局,这个很简单在FrameLayout中放置一个View和一个ListView:
<?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学习中。。。