Fresco对Listview等快速滑动时停止加载

Fresco对Listview等快速滑动时停止加载

Fresco中在listview之类的快速滑动时停止加载,滑动停止后恢复加载:

1.设置图片请求是否开启
  1. // 暂停图片请求  
  2. public static void imagePause() {  
  3.     Fresco.getImagePipeline().pause();
  4. }
  5. // 恢复图片请求  
  6. public static void imageResume() {  
  7.     Fresco.getImagePipeline().resume();
  8. }
2.设置ListView滚动监听
  1. <pre name=“code” class=”java”><pre name=”code” class=”java”>mListView.setOnScrollListener(new AbsListView.OnScrollListener() {  
  2.     @Override  
  3.     public void onScrollStateChanged(AbsListView view, int scrollState) {  
  4.         switch(scrollState){  
  5.             case AbsListView.OnScrollListener.SCROLL_STATE_IDLE://空闲状态  
  6.                 imageResume();
  7.                 break;  
  8.             case AbsListView.OnScrollListener.SCROLL_STATE_FLING://滚动状态  
  9.                 imagePause();
  10.                 break;  
  11.             case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL://触摸后滚动  
  12.                 break;  
  13.         }
  14.     }
  15.     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {  
  16.     }
  17. });

 

就是这么多,赶紧试试吧!

Android UI优化—Listview 重用convertView

1.重用convertView

我们对convertView添加判断,如果存在我们就直接使用,否则初始化一个convertView的实例。(如下图)

这里写图片描述

2.使用viewHolder

使用viewHolder并且是一个静态的匿名内部类。(如下图)

这里写图片描述

3.在列表里面有图片的情况下,监听滑动不加载图片

1.可以查看一下我的这篇文章Listview

2.这个建议用一些图片请求框架,如:Android-Universal-Image-Loader,推荐个中文解析的网站http://codekk.com/

4.多个不同布局,可以创建不同的viewHolder和convertView进行重用

比如聊天:左边一个布局,右边一个布局,我们可以创建不同的viewHolder,并且对convertView进行判断每个不同的convertView只初始化一次。

修复垂直滑动RecyclerView嵌套水平滑动RecyclerView水平滑动不灵敏问题

在 Android 应用中,大部分情况下都会使用一个垂直滚动的 View 来显示内容(比如 ListView、RecyclerView 等)。但是有时候你还希望垂直滚动的View 里面的内容可以水平滚动。如果直接在垂直滚动的 View 里面使用水平滚动的 View,则滚动操作并不是很流畅。

比如下图中的示例:

%title插图%num

为什么会出现这个问题呢?

上图中的布局为一个 RecyclerView 使用的是垂直滚动的 LinearLayoutManager 布局管理器,而里面每个 Item 为另外一个 RecyclerView 使用的是水平滚动的 LinearLayoutManager。而在 Android系统的事件分发中,即使*上层的 View 只能垂直滚动,当用户水平拖动的时候,*上层的 View 依然会拦截点击事件。下面是 RecyclerView.java 中 onInterceptTouchEvent 的相关代码:

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {  
  ...
 
  switch (action) {
    case MotionEvent.ACTION_DOWN:
        ...
 
    case MotionEvent.ACTION_MOVE: {
        ...
 
        if (mScrollState != SCROLL_STATE_DRAGGING) {
          boolean startScroll = false;
          if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
            ...
            startScroll = true;
          }
          if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
            ...
            startScroll = true;
          }
          if (startScroll) {
            setScrollState(SCROLL_STATE_DRAGGING);
          }
      }
    } break;
      ...
 
  }
  return mScrollState == SCROLL_STATE_DRAGGING;
}
 

注意上面的 if 判断:

if(canScrollVertically && Math.abs(dy) > mTouchSlop) {...}  
 

RecyclerView 并没有判断用户拖动的角度, 只是用来判断拖动的距离是否大于滚动的*小尺寸。 如果是一个只能垂直滚动的 View,这样实现是没有问题的。如果我们在里面再放一个 水平滚动的 RecyclerView ,则就出现问题了。

可以通过如下的方式来修复该问题:

if(canScrollVertically && Math.abs(dy) > mTouchSlop && (canScrollHorizontally || Math.abs(dy) > Math.abs(dx))) {...}  
 

下面是一个完整的实现 BetterRecyclerView.java :

public class BetterRecyclerView extends RecyclerView{
  private static final int INVALID_POINTER = -1;
  private int mScrollPointerId = INVALID_POINTER;
  private int mInitialTouchX, mInitialTouchY;
  private int mTouchSlop;
  public BetterRecyclerView(Contextcontext) {
    this(context, null);
  }
 
  public BetterRecyclerView(Contextcontext, @Nullable AttributeSetattrs) {
    this(context, attrs, 0);
  }
 
  public BetterRecyclerView(Contextcontext, @Nullable AttributeSetattrs, int defStyle) {
    super(context, attrs, defStyle);
    final ViewConfigurationvc = ViewConfiguration.get(getContext());
    mTouchSlop = vc.getScaledTouchSlop();
  }
 
  @Override
  public void setScrollingTouchSlop(int slopConstant) {
    super.setScrollingTouchSlop(slopConstant);
    final ViewConfigurationvc = ViewConfiguration.get(getContext());
    switch (slopConstant) {
      case TOUCH_SLOP_DEFAULT:
        mTouchSlop = vc.getScaledTouchSlop();
        break;
      case TOUCH_SLOP_PAGING:
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(vc);
        break;
      default:
        break;
    }
  }
 
  @Override
  public boolean onInterceptTouchEvent(MotionEvent e) {
    final int action = MotionEventCompat.getActionMasked(e);
    final int actionIndex = MotionEventCompat.getActionIndex(e);
 
    switch (action) {
      case MotionEvent.ACTION_DOWN:
        mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
        mInitialTouchX = (int) (e.getX() + 0.5f);
        mInitialTouchY = (int) (e.getY() + 0.5f);
        return super.onInterceptTouchEvent(e);
 
      case MotionEventCompat.ACTION_POINTER_DOWN:
        mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
        mInitialTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
        mInitialTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
        return super.onInterceptTouchEvent(e);
 
      case MotionEvent.ACTION_MOVE: {
        final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
        if (index < 0) {
          return false;
        }
 
        final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
        final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
        if (getScrollState() != SCROLL_STATE_DRAGGING) {
          final int dx = x - mInitialTouchX;
          final int dy = y - mInitialTouchY;
          final boolean canScrollHorizontally = getLayoutManager().canScrollHorizontally();
          final boolean canScrollVertically = getLayoutManager().canScrollVertically();
          boolean startScroll = false;
          if (canScrollHorizontally && Math.abs(dx) > mTouchSlop && (Math.abs(dx) >= Math.abs(dy) || canScrollVertically)) {
            startScroll = true;
          }
          if (canScrollVertically && Math.abs(dy) > mTouchSlop && (Math.abs(dy) >= Math.abs(dx) || canScrollHorizontally)) {
            startScroll = true;
          }
          return startScroll && super.onInterceptTouchEvent(e);
        }
        return super.onInterceptTouchEvent(e);
      }
 
      default:
        return super.onInterceptTouchEvent(e);
    }
  }
}
 

其他问题

当用户快速滑动(fling)RecyclerView 的时候, RecyclerView 需要一段时间来确定其*终位置。 如果用户在快速滑动一个子的水平 RecyclerView,在子 RecyclerView 还在滑动的过程中,如果用户垂直滑动,则是无法垂直滑动的。原因是子 RecyclerView 依然处理了这个垂直滑动事件。

%title插图%num

所以,在快速滑动后的滚动到静止的状态中,子 View 不应该响应滑动事件了,再次看看 RecyclerView 的 onInterceptTouchEvent() 代码:

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {  
    ...
 
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            ...
 
            if (mScrollState == SCROLL_STATE_SETTLING) {
                getParent().requestDisallowInterceptTouchEvent(true);
                setScrollState(SCROLL_STATE_DRAGGING);
            }
 
            ...
    }
    return mScrollState == SCROLL_STATE_DRAGGING;
}
 

可以看到,当 RecyclerView 的状态为 SCROLL_STATE_SETTLING (快速滑动后到滑动静止之间的状态)时, RecyclerView 告诉父控件不要拦截事件。

同样的,如果只有一个方向固定,这样处理是没问题的。

针对我们这个嵌套的情况,父 RecyclerView 应该只拦截垂直滚动事件,所以可以这么修改父 RecyclerView:

public class FeedRootRecyclerView extends BetterRecyclerView{  
  public FeedRootRecyclerView(Contextcontext) {
    this(context, null);
  }
 
  public FeedRootRecyclerView(Contextcontext, @Nullable AttributeSetattrs) {
    this(context, attrs, 0);
  }
 
  public FeedRootRecyclerView(Contextcontext, @Nullable AttributeSetattrs, int defStyle) {
    super(context, attrs, defStyle);
  }
 
  @Override
  public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    /* do nothing */
  }
}
 

下图为*终的结果:

%title插图%num

如果感兴趣可以下载 示例项目 ,注意示例项目中使用 kotlin,所以需要配置 kotlin 插件。

Android 各种 滚动View 监听 和各种判断

总结下各种View 的滑动监听

滑动阈值:int touchSlop = ViewConfiguration.get(this).getScaledTouchSlop();

getMeasuredHeight()是实际View的大小,与屏幕无关,而getHeight的大小此时则是屏幕的大小。

当超出屏幕后, getMeasuredHeight() 等于 getHeight()加上屏幕之外没有显示的大小

滚动到顶部判断:
getScrollY() == 0

滚动到底部判断:
View childView = getChildAt(0);

childView.getMeasuredHeight() <= getScrollY() + getHeight();

其中getChildAt表示得到ScrollView的child View
childView.getMeasuredHeight()表示得到子View的高度,

getScrollY()表示得到y轴的滚动距离,

getHeight()为scrollView可见的高度即屏幕的高度
getScrollY()达到*大时加上scrollView的高度就的就等于它内容的高度了.

ScroolView:
回到顶部:

一、ScrollView.scrollTo(0,0)  直接置顶,瞬间回到顶部,没有滚动过程,其中Y值可以设置为大于0的值,使Scrollview停在指定位置;

二、ScrollView.fullScroll(View.FOCUS_UP)  类似于手动拖回顶部,有滚动过程;   需要post 新开线程

三、ScrollView.smoothScrollTo(0, 0) 类似于手动拖回顶部,有滚动过程,其中Y值可以设置为大于0的值,使Scrollview停在指定位置。
判断滑动位置的地方,可以有两种方式:

1、实现OnTouchListener来监听

OnTouchListener onTouchListener=new OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
if (childView != null && childView .getMeasuredHeight() <= getScrollY() + getHeight()) {
// 滑动到底部
} else if (getScrollY() == 0) {
// 滑动到顶部 貌似不好用
//看下面listview 判断 *个子view 的getTop
}
break;
}
return false;
}
}
2、重写ScrollView的onScrollChanged的方法,在onScrollChanged函数中判断

public class myScrollView extends ScrollView
{
public myScrollView(Context context)
{
super(context);
}
public myScrollView(Context context, AttributeSet attributeSet)
{
super(context,attributeSet);
}

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt)
{
View view = (View)getChildAt(getChildCount()-1);
int d = view.getBottom();
d -= (getHeight()+getScrollY());
if(d==0)
{
//you are at the end of the list in scrollview
//do what you wanna do here
}
else
super.onScrollChanged(l,t,oldl,oldt);
}
}
判断是否在顶部  在滚动停止后:

private int lastY = 0;
private int touchEventId = -9983761;

@SuppressLint(“HandlerLeak”)
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
View scroller = (View) msg.obj;

if (msg.what == touchEventId) {
if (lastY == scroller.getScrollY()) {
//停止了,此处你的操作业务
if (scroller.getScrollY() == 0) {
hidePopup();
}
} else {
handler.sendMessageDelayed(handler.obtainMessage(touchEventId, scroller), 1);
lastY = scroller.getScrollY();
}
}
}
};
case MotionEvent.ACTION_CANCEL:
if (type == SCROLLVIEW) {
// hidePopup(); 从整个Messge池中返回一个新的Message实例 降低内存的开销 避免分配新的对象
handler.sendMessageDelayed(handler.obtainMessage(touchEventId, v), 300);
}
break;
判断滑动方向:

1. onTouch 监听

package com.example.testtt;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.widget.Toast;
public class MainActivity extends Activity {
//手指按下的点为(x1, y1)手指离开屏幕的点为(x2, y2)
float x1 = 0;
float x2 = 0;
float y1 = 0;
float y2 = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
//继承了Activity的onTouchEvent方法,直接监听点击事件
if(event.getAction() == MotionEvent.ACTION_DOWN) {
//当手指按下的时候
x1 = event.getX();
y1 = event.getY();
}
if(event.getAction() == MotionEvent.ACTION_UP) {
//当手指离开的时候
x2 = event.getX();
y2 = event.getY();
if(y1 – y2 > 50) {
Toast.makeText(MainActivity.this, “向上滑”, Toast.LENGTH_SHORT).show();
} else if(y2 – y1 > 50) {
Toast.makeText(MainActivity.this, “向下滑”, Toast.LENGTH_SHORT).show();
} else if(x1 – x2 > 50) {
Toast.makeText(MainActivity.this, “向左滑”, Toast.LENGTH_SHORT).show();
} else if(x2 – x1 > 50) {
Toast.makeText(MainActivity.this, “向右滑”, Toast.LENGTH_SHORT).show();
}
}
return super.onTouchEvent(event);
}

}
2. 继承SrcoolView

/使用自定义view继承自ScrollView
public class MyScrollView extends ScrollView {

private OnScrollListener listener;

public void setOnScrollListener(OnScrollListener listener) {
this.listener = listener;
}

public MyScrollView(Context context) {
super(context);
}

public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

//设置接口
public interface OnScrollListener{
void onScroll(int scrollY);
}

//重写原生onScrollChanged方法,将参数传递给接口,由接口传递出去
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if(listener != null){

//这里我只传了垂直滑动的距离
listener.onScroll(t);
}
}
}

//1. *个参数是目前水平滑动后的距离
//2. 第二个参数是目前垂直滑动后的距离
//3. 第三个参数是之前水平滑动前的距离
//4. 第四个参数是之前水平滑动前的距离
ListView 和GridView 监听
移动到顶部底部:

listview.setSelection(position);

gridview.smoothScrollToPosition(position);  带动画   post新开线程

需要在新线程使用

滚动监听:

private AbsListView.OnScrollListener LIST_GRID_LISTENER = new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case SCROLL_STATE_FLING:
break;
case SCROLL_STATE_IDLE:
// 判断滚动到顶部
if (mListView.getFirstVisiblePosition() == 0) {
View firstVisibleItemView = view.getChildAt(0);
if (firstVisibleItemView != null && firstVisibleItemView.getTop() == 0) {

}
}
break;
case SCROLL_STATE_TOUCH_SCROLL:

break;
}
}

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem == 0) {

//顶部 不够准确
}
}
};
ScroolState 三种状态

OnScrollListener.SCROLL_STATE_IDLE:滚动停止时的状态
OnScrollListener.SCROLL_STATE_STOUCH_SCROLL:触摸正在滚动,手指还没离开界面时的状态
OnScrollListener.SCROLL_STATE_FLING:用户在用力滑动后,ListView由于惯性将继续滑动时的状态

onScroll 介绍

firstVisibleItem:当前能看见的*个item的ID(从0开始)
visibleItemCount:当前可见的item总数
totalItemCount:列表中适配器总数量,也就是整个ListView中item总数
滑动方向:

firstVisibleItem > oldVisibleItem   // 向上滑动
firstVisibleItem < oldVisibleItem  // 向下滑动
判断顶部底部

firstVisibleItem + visibleItemCount == totalItemCount    //底部
firstVisibleItem=0  顶部
在onScrollStateChanged中判断
mListView.getLastVisiblePosition() == (mListView.getCount() – 1)  //底部
mListView.getFirstVisiblePosition() == 0   //顶部
ListView也为我们提供了一些封装好了的方法,来获取item的位置信息

// 获取当前可见区域内*个item            mListView.getFirstVisiblePosition();

// 获取当前可见区域内*后一个item       mListView.getLastVisiblePosition();

 

RecyclerView :
移动:

RecyclerView提供了几种移动的方法

scrollToPosition       这个方法的作用是定位到指定项,就是把你想显示的项显示出来,但是在屏幕的什么位置是不管的,只要那一项现在看得到了,那它就罢工了!

scrollTo

scrollBy   这个方法是自己去控制移动的距离,单位是像素,所以在使用scrollBy(x, y)需要自己去计算移动的高度或宽度。

smoothScrollBy

smoothScrollToPosition      惯性滑动至某一位置

scrollToPositionWithOffset   用法:((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(position,0);

该置顶就置顶

判断 顶部 底部
方法一:

垂直判断:

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
Log.i(TAG, “————————————–“);
if(mRecyclerView.canScrollVertically(1)){
Log.i(TAG, “direction 1: true”);
}else {
Log.i(TAG, “direction 1: false”);//滑动到底部
}
if(mRecyclerView.canScrollVertically(-1)){
Log.i(TAG, “direction -1: true”);
}else {
Log.i(TAG, “direction -1: false”);//滑动到顶部
}
}
});
横向:

/**
* Check if this view can be scrolled horizontally in a certain direction.
*
* @param direction Negative to check scrolling left, positive to check scrolling right.
* @return true if this view can be scrolled in the specified direction, false otherwise.
*/
public boolean canScrollHorizontally(int direction) {
final int offset = computeHorizontalScrollOffset();
final int range = computeHorizontalScrollRange() – computeHorizontalScrollExtent();
if (range == 0) return false;
if (direction < 0) {
return offset > 0;
} else {
return offset < range – 1;
}
}
判断是否滑动到底部, recyclerView.canScrollVertically(1);返回false表示不能往上滑动,即代表到底部了;

判断是否滑动到顶部, recyclerView.canScrollVertically(-1);返回false表示不能往下滑动,即代表到顶部了;

方法二:
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
Log.i(TAG, “————————————–“);
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int firstCompletelyVisibleItemPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
Log.i(TAG, “firstCompletelyVisibleItemPosition: “+firstCompletelyVisibleItemPosition);
if(firstCompletelyVisibleItemPosition==0)
Log.i(TAG, “滑动到顶部”);

int lastCompletelyVisibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition();
Log.i(TAG, “lastCompletelyVisibleItemPosition: “+lastCompletelyVisibleItemPosition);
if(lastCompletelyVisibleItemPosition==layoutManager.getItemCount()-1)
Log.i(TAG, “滑动到底部”);
}
});
滑动方向:

//dy <0 表示 上滑, dy>0 表示下滑

三种状态:

/**
* The RecyclerView is not currently scrolling.
* 当前的recycleView不滑动(滑动已经停止时)
*/
public static final int SCROLL_STATE_IDLE = 0;

/**
* The RecyclerView is currently being dragged by outside input such as user touch input.
* 当前的recycleView被拖动滑动
*/
public static final int SCROLL_STATE_DRAGGING = 1;

/**
* The RecyclerView is currently animating to a final position while not under
* outside control.
* 当前的recycleView在滚动到某个位置的动画过程,但没有被触摸滚动.调用 scrollToPosition(int) 应该会触发这个状态
*/
public static final int SCROLL_STATE_SETTLING = 2;

Android中 ListView是*常用的控件之一

前言:

ListView这个列表控件在Android中是*常用的控件之一,几乎在所有的应用程序中都会使用到它。

目前正在做的一个记账本APP中就用到了它,主要是用它来呈现收支明细,是一个图文列表的呈现方式,下面就讲讲具体是如何实现的。

 

效果图:

该功能是在另一篇博文【Android Studio 使用ViewPager + Fragment实现滑动菜单Tab效果 –简易版】的基础上进行添加的

%title插图%num

 

实现的思路:

1、该功能是用fragment来做布局的,首先创建一个fragment.xml布局文件,在里面添加一个ListView控件;

2、由于List里面既要呈现图片,也要呈现文字,所以再创建一个fragment_item.xml布局文件,在里面添加ImageView、TextView,用来显示图片和文字;

3、使用SimpleAdapter来绑定数据;

 

具体实现逻辑:

1、创建fragment_one.xml

1 <ListView
2     android:id="@+id/lv_expense"
3     android:layout_width="match_parent"
4     android:layout_height="wrap_content">
5 
6 </ListView>

 

2、创建fragment_one_item.xml

 

 1 <ImageView
 2     android:id="@+id/image_expense"
 3     android:layout_width="wrap_content"
 4     android:layout_height="wrap_content"
 5     android:paddingTop="10dp"
 6     android:paddingRight="10dp"
 7     android:paddingBottom="10dp"
 8     android:adjustViewBounds="true"
 9     android:maxWidth="72dp"
10     android:maxHeight="72dp"/>
11 <TextView
12     android:id="@+id/tv_expense_category"
13     android:layout_width="wrap_content"
14     android:layout_height="wrap_content"
15     android:layout_weight="1"
16     android:padding="10dp"/>
17 <TextView
18     android:id="@+id/tv_expense_money"
19     android:layout_width="wrap_content"
20     android:layout_height="wrap_content"
21     android:text="10.0000"/>

 

3、主逻辑OneFragment.java

 1 List<Map<String, Object>> listitem = new ArrayList<Map<String, Object>>(); //存储数据的数组列表
 2 //写死的数据,用于测试
 3 int[] image_expense = new int[]{R.mipmap.detail_income, R.mipmap.detail_payout }; //存储图片
 4 String[] expense_category = new String[] {"发工资", "买衣服"};
 5 String[] expense_money = new String[] {"30000.00", "1500.00"};
 6 for (int i = 0; i < image_expense.length; i++)
 7 {
 8     Map<String, Object> map = new HashMap<String, Object>();
 9     map.put("image_expense", image_expense[i]);
10     map.put("expense_category", expense_category[i]);
11     map.put("expense_money", expense_money[i]);
12     listitem.add(map);
13 }
14 
15 //创建适配器
16 // *个参数是上下文对象
17 // 第二个是listitem
18 // 第三个是指定每个列表项的布局文件
19 // 第四个是指定Map对象中定义的两个键(这里通过字符串数组来指定)
20 // 第五个是用于指定在布局文件中定义的id(也是用数组来指定)
21 SimpleAdapter adapter = new SimpleAdapter(getActivity()
22         , listitem
23         , R.layout.fragment_one_item
24         , new String[]{"expense_category", "expense_money", "image_expense"}
25         , new int[]{R.id.tv_expense_category, R.id.tv_expense_money, R.id.image_expense});
26         
27 ListView listView = (ListView) v.findViewById(R.id.lv_expense);
28 listView.setAdapter(adapter);

 

以上就是整个功能实现的逻辑,本文代码中,List中呈现的数据是写死的,从数据库中获取源数据的方式可以参考我的源代码,且使用的数据库是SQLite。

源代码:https://github.com/AnneHan/ListViewDemo

SQLite数据库的使用:Android Studio 通过一个登录功能介绍SQLite数据库的使用