android listview item點擊展開,androidlistview
最近做項目真是頭疼呢?之前想用ListViewAnnotation來著,就是可以實作類別似於android 通知欄滑動刪除的效果。好像是一位大牛自己一個人寫的吧。我在這裡首先向他致敬。不過,話說回來,實現原理也是比較易於理解的,就是檢測你的滑動距離以及速度,再作出判斷,進行操作。具體大家參考下Google keep的兩種列表模式下滑動刪除的操作就理解類。
非常不幸的事,我再布局中用了fragment和biewpager,所以產生了手勢衝突,我為此改寫了library裡的手勢操作檢測方法,最後勉強改出來的。但是!!!!效果實在慘不忍睹,只好作罷,最後,思來想去,我決定使用listview item點擊展開來實現我想要的效果。
好吧,廢話說了很多,終於要進入正題了。
首先補充三點知識:
第一點:
getLayoutParams()方法和setLayoutParams()用法首先,我們利用getLayoutParams()來獲得指定控制項的LayoutParams。例如:LinearLayout.LayoutParams lp = (RelativeLayout.LayoutParams) deletButton.getLayoutParams();然後,我們可以為deleteButton設定layoutparams屬性,例如: lp.width = btnWidth; lp.leftMargin = 10;最後,我們將設定好的layoutparams屬性設定給指定的控制項: deletButton.setLayoutParams(lp);
第二點:
public final int getMeasuredHeight ()Added in API level 1Like getMeasuredHeightAndState(), but only returns the raw width component (that is the result is masked by MEASURED_SIZE_MASK).ReturnsThe raw measured height of this view.public final int getHeight ()Added in API level 1Return the height of your view.ReturnsThe height of your view, in pixels.getMeasuredHeight()返回的是原始測量高度,與螢幕無關,getHeight()返回的是在螢幕上顯示的高度。實際上在當螢幕可以包裹內容的時候,他們的值是相等的,只有當view超出螢幕後,才能看出他們的區別。當超出螢幕後,getMeasuredHeight()等於getHeight()加上螢幕之外沒有顯示的高度。
第三點:
MeasureSpec的使用。MeasureSpec一般出現在自訂View中,因為在自訂 view中我們經常需要重寫該方法,由它來指定自訂控制項在螢幕上的大小。protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)onMeasure傳入的兩個參數是由上一層控制項傳入的大小,有多種情況,重寫該方法時需要對計算控制項的實際大小,然後調用setMeasuredDimension(int, int)設定實際大小。onMeasure傳入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸數值,而是將模式和尺寸組合在一起的數值。我們需要通過int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。mode共有三種情況,取值分別為MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。MeasureSpec.EXACTLY是精確尺寸,當我們將控制項的layout_width或layout_height指定為具體數值時如andorid:layout_width="50dip",或者為FILL_PARENT是,都是控制項大小已經確定的情況,都是精確尺寸。MeasureSpec.AT_MOST是最大尺寸,當控制項的layout_width或layout_height指定為WRAP_CONTENT時,控制項大小一般隨著控制項的子空間或內容進行變化,此時控制項尺寸只要不超過父控制項允許的最大尺寸即可。因此,此時的mode是AT_MOST,size給出了父控制項允許的最大尺寸。MeasureSpec.UNSPECIFIED是未指定尺寸,這種情況不多,一般都是父控制項是AdapterView,通過measure方法傳入的模式。然後,我們可以調用MeasureSpec.makeMeasureSpec(int size, int mode)來進行設定。
好,現在先上一張demo,使用者可以自己進行擴充。
哈哈,效果還行吧。那個,表徵圖略顯呆萌,還請不要見笑(自己繪製的,苦逼程式員一隻啊)。
好,接下來,我們來看一下動效的實現。當然,先上源碼。
package wenyue.expandlistview.me;import android.util.Log;import android.view.View;import android.view.animation.Animation;import android.view.animation.Transformation;import android.widget.LinearLayout.LayoutParams; /** * animation * 2014年十一月十八日 * 蒼狼問月 * canglangwenyue.github.io * @author canglangwenyue * */public class ViewExpandAnimation extends Animation {private View mAnimationView = null;private LayoutParams mViewLayoutParams = null;private int mStart = 0;private int mEnd = 0;public ViewExpandAnimation(View view){ animationSettings(view, 500);}public ViewExpandAnimation(View view, int duration){ animationSettings(view, duration);}private void animationSettings(View view, int duration){ setDuration(duration); mAnimationView = view; mViewLayoutParams = (LayoutParams) view.getLayoutParams(); mStart = mViewLayoutParams.bottomMargin; mEnd = (mStart == 0 ? (0 - view.getHeight()) : 0); view.setVisibility(View.VISIBLE);}@Overrideprotected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); if(interpolatedTime < 1.0f){ mViewLayoutParams.bottomMargin = mStart + (int) ((mEnd - mStart) * interpolatedTime); // invalidate mAnimationView.requestLayout(); }else{ mViewLayoutParams.bottomMargin = mEnd; mAnimationView.requestLayout(); if(mEnd != 0){ mAnimationView.setVisibility(View.GONE); } } Log.i("hehe", "interpolatedTime = " + interpolatedTime + " , bottomMargin" + mViewLayoutParams.bottomMargin);}}動效實現的代碼不多,setDuration(duration)設定動效的期間。之後調用RelitaveLayout.bottomMargin;對於該屬性developer文檔解釋為The bottom margin in pixels of the child.mAnimationView.requestLayout();該方法內部會調用Object.layout(...)方法,來重會布局。
至於,listview adapter的重寫,就比較簡單了。所以,不再貼出全部源碼。只說一下需要注意的地方。
首先,你需要在getView(…)中載入自訂listview 的item布局檔案:
convertView = myInflater.inflate(R.layout.expand_item, null);
接下來,貼出相應布局檔案代碼:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:padding="5dip" ><RelativeLayout android:id="@+id/header" android:layout_width="match_parent" android:layout_height="wrap_content" > <LinearLayout android:id="@+id/appInfo" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:orientation="vertical" > <TextView android:id="@+id/tvName" android:layout_width="match_parent" android:layout_height="45dp" android:gravity="center|left" android:text="name" android:textColor="#000000" android:layout_marginLeft="20.0dip" android:textSize="16sp" /> </LinearLayout></RelativeLayout><RelativeLayout android:id="@+id/footer" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ffffff" > <Button android:id="@+id/btnOpen" android:layout_width="100dip" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_marginTop="6dip" android:focusable="false" android:textColor="#000000" android:background="@drawable/editbutton" android:textSize="16sp" /> <Button android:id="@+id/btnView" android:layout_width="100dip" android:layout_height="wrap_content" android:layout_marginTop="6dip" android:layout_toRightOf="@id/btnOpen" android:focusable="false" android:background="@drawable/start" android:textColor="#000000" android:textSize="16sp" /> <Button android:id="@+id/btnWarning" android:layout_width="100dip" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginTop="6dip" android:focusable="false" android:background="@drawable/tomatodeletebutton" android:textColor="#000000" android:textSize="16sp" /></RelativeLayout></LinearLayout>
我設定的是預設在activity onStart時嵌套布局中的第二個RelativeLayout布局檔案的內容是被隱藏的。
在BaseAdapter中的設定如下:
RelativeLayout footer = (RelativeLayout) convertView .findViewById(R.id.footer); int widthSpec = MeasureSpec.makeMeasureSpec( (int) (mLcdWidth - 10 * mDensity), MeasureSpec.EXACTLY); footer.measure(widthSpec, 0); LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) footer .getLayoutParams(); params.bottomMargin = -footer.getMeasuredHeight(); footer.setVisibility(View.GONE);
然後,是在onCreate方法中設定setOnItemClickListener方法,在其中設定點擊item後,開始動效,再次點擊則隱藏,且同一時間只有一個item可以展開(因為個人認為使用者不可能在同一時間操作兩個 item的隱藏操作),代碼如下:
if (mLastTouchTag != null) { View temp = arg0.findViewWithTag(mLastTouchTag); if (temp != null) { View footTemp = temp.findViewById(R.id.footer); if (footTemp != null && (footTemp.getVisibility() != View.GONE)) { footTemp.startAnimation(new ViewExpandAnimation( footTemp)); } } } mLastTouchTag = (ViewHolder) v.getTag(); // onion555 end View footer = v.findViewById(R.id.footer); footer.startAnimation(new ViewExpandAnimation(footer));
好吧,就寫到這裡吧,有什麼問題,請留言。同樣,在最後附上源碼。
點擊開啟連結