生活本來就不容易啊,而我們的不努力只會讓生活變得更加無賴。
相信大家在學習過程當中對ListView一定不會陌生,基本我們要用到列表時第一時間會想到它,雖然在Android5.0時Google推出了RecyclerView企圖取代它,而且在這段期間內RecyclerView基本上已經可以滿足取代ListView的能力(我始終覺得秒現瀑布流效果非常牛),但我覺得還是需要認真學習ListView,這兩天結合《Android群英傳》的學習,寫一下ListView的學習筆記。
ListView的簡單使用:
- activity_main.xml
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@android:color/transparent"
android:dividerHeight="1dp"
android:id="@+id/list_view"/>```
* 代碼使用:
* 適配器MyAdapter
public class MyAdapter extends BaseAdapter{
private List<String> list;
private LayoutInflater inflater;
public MyAdapter(Context context ,List<String> list) {
this.list = list;
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
viewHolder viewHolder = null;
if (view == null){
viewHolder = new viewHolder();
view = inflater.inflate(R.layout.view_holder,null);
viewHolder.textView = (TextView) view.findViewById(R.id.text_view);
view.setTag(viewHolder);
}else{
viewHolder = (MyAdapter.viewHolder) view.getTag();
}
viewHolder.textView.setText(list.get(i));
return view;
}
class viewHolder{
TextView textView;
}
}```
- MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnTouchListener {
private List<String> list ;
private MyAdapter adapter;
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
listView = (ListView) findViewById(R.id.list_view);
list = initList();
adapter = new MyAdapter(this,list);
listView.setAdapter(adapter);
//設置ListView為空時的顯示圖片
listView.setEmptyView(findViewById(R.id.emptyImage));
//滑動監聽事件
listView.setOnTouchListener(this);
//設置點擊事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
}
});
}
private List<String> initList() {
List<String> mdatas = new ArrayList<>();
for (int i = 'A';i<'z';++i){
mdatas.add(""+(char)i);
}
return mdatas;
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_DOWN:
//觸摸時調用 Toast.makeText(this,"Down",Toast.LENGTH_SHORT).show();
break;
case MotionEvent.ACTION_MOVE:
//滑動時調用
Toast.makeText(this,"Move",Toast.LENGTH_SHORT).show();
break;
case MotionEvent.ACTION_UP:
//離開時調用
Toast.makeText(this,"Up",Toast.LENGTH_SHORT).show();
break;
}
return false;
}
}```
* 效果

------
### 常用屬性:
- 設置ListView為空時的顯示布局
//設置ListView為空時的顯示圖片
listView.setEmptyView(findViewById(R.id.emptyImage));```
- 動態增加數據及設置ListView位置
/**
* 動態增加
*/
public void btnAdd(){
list.addAll(Arrays.asList("Hello","Hi","WelCome"));
adapter.notifyDataSetChanged();
listView.setSelection(0);
}```
- 具有彈性的ListView
/**
*修改overScrollBy方法中的maxOverScrollY值
/
private int mMaxOverSrcollY = 5;
//適配分辨率
private void getmMaxOverSrcollY(){
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
float density = metrics.density;
mMaxOverSrcollY = (int) (densitymMaxOverSrcollY);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY, scrollX,
scrollY, scrollRangeX, scrollRangeY, maxOverScrollX,
mMaxOverSrcollY, isTouchEvent);
}```
為ListView設置滑動時Toolbar的隱藏
其實邏輯很簡單,就是為ListView設置headView,然后借助OnTouchListener獲取滑動點的坐標改變大小,實現滑上滑下距離的判斷.最后借助簡單的動畫實現隱藏和出現.代碼
//獲取系統認為的最低滑動距離
private float mTouchSlop;
//定義兩個變量進行兩次滑動距離的判斷
private float mFirstY , mCurrentY;
//聲明flag標志Toolbar狀態
private int direction;
//聲明boolean判斷狀態
private boolean mShow;
private Toolbar mToorbar;
private Animator mAnimator;
mToorbar = (Toolbar) findViewById(R.id.tool_bar);
setSupportActionBar(mToorbar);
//添加headView
View head = new View(this);
head.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,
(int) getResources().getDimension(R.dimen.abc_action_bar_default_height_material)));
listView.addHeaderView(head);
//獲取最低滑動距離
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_DOWN:
//觸摸時調用,獲取第一次位置
mFirstY = motionEvent.getY();
break;
case MotionEvent.ACTION_MOVE:
//滑動時調用
mCurrentY = motionEvent.getY();
if (mCurrentY - mFirstY > mTouchSlop){
direction = 0;//向下滑
}else if (mFirstY - mCurrentY >mTouchSlop){
direction = 1;//向上滑
}
if (direction == 1){
if (mShow){
toolbarAnim(0);//隱藏
mShow = !mShow;
}
}else if (direction == 0){
if (!mShow){
toolbarAnim(1);
mShow = !mShow;
}
}
break;
case MotionEvent.ACTION_UP:
//離開時調用
break;
}
return false;
}
//控制head布局狀態
private void toolbarAnim(int flag) {
if (mAnimator != null && mAnimator.isRunning()){
mAnimator.cancel();
}
if (flag == 0){
mAnimator = ObjectAnimator.ofFloat(mToorbar,"translationY",
mToorbar.getTranslationY(),0);
}else {
mAnimator = ObjectAnimator.ofFloat(mToorbar,"translationY",
mToorbar.getTranslationY(),mToorbar.getHeight());
}
mAnimator.start();
}```
* 效果

### 常見拓展
在學習ListView的,順便學習了一下Hyman的一篇技術博客,是關于仿QQ實現左劃刪除的效果的,其實代碼的邏輯也很簡單,就是利用事件分發機制實現手勢的判斷,利用dispatchTouchEvent以及onTouchEvent這兩個方法對手勢進行判斷,最后利用Button對所在Item進行delete
* 整體預覽

* xml文件
* enter_ainm.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:fromXScale="0.0"
android:toXScale="1.0"
android:fromYScale="0.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:duration="200"/>
<alpha
android:interpolator="@android:anim/decelerate_interpolator"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="200"/>
</set>```
- exit_ain.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:fromXScale="1.0"
android:toXScale="0.5"
android:fromYScale="1.0"
android:toYScale="0.5"
android:pivotX="50%"
android:pivotY="50%"
android:duration="500"/>
<alpha
android:interpolator="@android:anim/accelerate_interpolator"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="500"/>
</set>```
* del_btn.xml
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:id="@+id/btn_del"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="刪除"
android:textColor="#fff"
android:background="@color/colorPrimary"
android:layout_marginTop="5dp" >
</Button>```
- activity_main.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.lqg.deletedemo.MainActivity">
<com.lqg.deletedemo.MyListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/delete_listview">
</com.lqg.deletedemo.MyListView>
</RelativeLayout>```
* style.xml
<style name="btn_del_Animotion" parent="android:Animation" mce_bogus="1" >
<item name="android:windowEnterAnimation">@anim/enter_anim</item>
<item name="android:windowExitAnimation">@anim/exit_ain</item>
</style>```
- MyListView.java
public class MyListView extends ListView {
/**
* 用戶滑動的最小距離
*/
private int mTouchSlop;
/**
* 是否響應滑動
*/
private boolean isSliding;
/**
* 手指按下時的x坐標
*/
private int FirstX;
/**
* 手指按下時的y坐標
*/
private int FirstY;
/**
* 手指移動時的x坐標
*/
private int NextX;
/**
* 手指移動時的y坐標
*/
private int NextY;
private LayoutInflater mInflater;
private PopupWindow mPopupWindow;
private int mPopupWindowHeight;
private int mPopupWindowWidth;
private Button mDelBtn;
/**
* 為刪除按鈕提供一個回調接口
*/
private DelButtonClickListener mListener;
/**
* 當前手指觸摸的View
*/
private View mCurrentView;
/**
* 當前手指觸摸的位置
*/
private int mCurrentViewPos;
/**
* 必要的一些初始化
*
*/
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
mInflater = LayoutInflater.from(context);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
View view = mInflater.inflate(R.layout.del_btn, null);
mDelBtn = (Button) view.findViewById(R.id.btn_del);
mPopupWindow = new PopupWindow(view, LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
/**
* 先調用下measure,否則拿不到寬和高
*/
mPopupWindow.getContentView().measure(0, 0);
mPopupWindowHeight = mPopupWindow.getContentView().getMeasuredHeight();
mPopupWindowWidth = mPopupWindow.getContentView().getMeasuredWidth();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
FirstX = x;
FirstY = y;
/**
* 如果當前popupWindow顯示,則直接隱藏,然后屏蔽ListView的touch事件的下傳
*/
if (mPopupWindow.isShowing()) {
dismissPopWindow();
return false;
}
// 獲得當前手指按下時的item的位置
mCurrentViewPos = pointToPosition(FirstX, FirstY);
// 獲得當前手指按下時的item
View view = getChildAt(mCurrentViewPos - getFirstVisiblePosition());
mCurrentView = view;
break;
case MotionEvent.ACTION_MOVE:
NextX = x;
NextY = y;
int dx = NextX - FirstX;
int dy = NextY - FirstY;
/**
* 判斷是否是從右到左的滑動
*/
if (NextX < FirstX && Math.abs(dx) > mTouchSlop && Math.abs(dy) < mTouchSlop) {
isSliding = true;
}
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
/**
* 如果是從右到左的滑動才相應
*/
if (isSliding) {
switch (action) {
case MotionEvent.ACTION_MOVE:
int[] location = new int[2];
// 獲得當前item的位置x與y
mCurrentView.getLocationOnScreen(location);
// 設置popupWindow的動畫
mPopupWindow.setAnimationStyle(R.style.btn_del_Animotion);
mPopupWindow.update();
mPopupWindow.showAtLocation(mCurrentView, Gravity.START | Gravity.TOP,
location[0] + mCurrentView.getWidth(), location[1] + mCurrentView.getHeight() / 2
- mPopupWindowHeight / 2);
// 設置刪除按鈕的回調
mDelBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.clickHappend(mCurrentViewPos);
mPopupWindow.dismiss();
}
}
});
break;
case MotionEvent.ACTION_UP:
isSliding = false;
}
// 相應滑動期間屏幕itemClick事件,避免發生沖突
return true;
}
return super.onTouchEvent(ev);
}
/**
* 隱藏popupWindow
*/
private void dismissPopWindow() {
if (mPopupWindow != null && mPopupWindow.isShowing()) {
mPopupWindow.dismiss();
}
}
public void setDelButtonClickListener(DelButtonClickListener listener) {
this.mListener = listener;
}
public interface DelButtonClickListener {
public void clickHappend(int position);
}
}```
* MainActivity.java
public class MainActivity extends AppCompatActivity {
private MyListView listView;
private ArrayAdapter<String> adapter;
private List<String> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView =(MyListView) findViewById(R.id.delete_listview);
list = new ArrayList<>();
for (int i = 1;i<100;++i){
list.add(""+i);
}
adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,list);
listView.setAdapter(adapter);
listView.setDelButtonClickListener(new MyListView.DelButtonClickListener() {
@Override
public void clickHappend(int position) {
adapter.remove(adapter.getItem(position));
adapter.notifyDataSetChanged();
}
});
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { Toast.makeText(MainActivity.this,position+"onClick",Toast.LENGTH_SHORT).show();
}
});
}
}```
- 代碼如上,其實這相當大部分是模仿hyman的代碼,不過出于學習的目的.所以寫在自己的筆記.HongYang博客傳送門
http://blog.csdn.net/lmj623565791/ - 效果