使用Endless / sth else加载更多JSON数据?

时间:2014-08-25 02:17:21

标签: android json listview lazy-loading

我有一个餐厅查找器应用程序,可以从JSON文件中检索数据。

现在我有大约313家餐厅,并且我已成功地在我的自定义列表视图中查看它们。现在我的问题是,因为很多数据和更多数据将很快添加,我想在滚动上加载更多数据,因为它已经需要几秒钟才能加载。

这是我的JSON文件:http://www.petuuk.com/android/allRestaurantList2.php

我有这个" SearchAll" class,用于加载的所有代码,用户在我的Mainactivity中单击Search All按钮后进入。

如果有人能够特别展示 WHERE和WHAT 代码来成功实现滚动更多滚动,那真的会有所帮助。

我是新手,所以只是提供链接并不是那么多。也许你可以帮我实现无限的适配器,这应该是我的理想选择?

public class SearchAll extends ListActivity {

ConnectionDetector cd;
AlertDialogManager alert = new AlertDialogManager();

//Progress Dialog
private ProgressDialog pDialog;

//make json parser Object
JSONParser jsonParser = new JSONParser();

ArrayList<HashMap<String, String>> restaurant_list;

//Restaurant Json array
JSONArray restaurants = null;

private static final 
String URL_RESTAURANT_LIST = "http://www.petuuk.com/android/allRestaurantList2.php";

//all JSON Node Names
private static final String TAG_ID = "login_id";
private static final String TAG_NAME = "name";
private static final String TAG_LOCATION = "location";
private static final String TAG_RATING = "rating";


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

    cd = new ConnectionDetector(getApplicationContext());

    //Check for Internet Connection
    if (!cd.isConnectingToInternet()) {
        //Internet connection not present
        alert.showAlertDialog(SearchAll.this, "Internet Connection Error",
                "Please Check Your Internet Connection", false);
        //stop executing code by return
        return;
    }

    restaurant_list = new ArrayList<HashMap<String, String>>();

    new LoadRestaurants().execute();

   //get ListView
    ListView lv = getListView();
    lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

    Intent intent = new Intent(getApplicationContext(), RestaurantProfile.class);
            String loginId = ((TextView) view.
                    findViewById(R.id.login_id)).
                    getText().toString();

            String res_name = ((TextView) view.
                    findViewById(R.id.restaurant_name)).
                    getText().toString();


            intent.putExtra(TAG_ID, loginId);
            intent.putExtra(TAG_NAME, res_name);

            startActivity(intent);


        }
    });


}


class LoadRestaurants extends AsyncTask<String, String, String> {

    //Show Progress Dialog
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        pDialog = new ProgressDialog(SearchAll.this);
        pDialog.setMessage("Loading All Restaurants...");
        pDialog.setIndeterminate(false);
        pDialog.setCancelable(false);
        pDialog.show();
    }

    protected String doInBackground(String... arg) {
        //building parameters
        List<NameValuePair> params = new ArrayList<NameValuePair>();

        //Getting JSON from URL
        String json = jsonParser.makeHttpRequest(URL_RESTAURANT_LIST, "GET", params);

        //Log Cat Response Check
        Log.d("Areas JSON: ", "> " + json);

        try {
            restaurants = new JSONArray(json);

            if (restaurants != null) {
                //loop through all restaurants
                for (int i = 0; i < restaurants.length(); i++) {
                    JSONObject c = restaurants.getJSONObject(i);

                    //Storing each json  object in the variable.
                    String id = c.getString(TAG_ID);
                    String name = c.getString(TAG_NAME);
                    String location = c.getString(TAG_LOCATION);
                    String rating = c.getString(TAG_RATING);

                    //Creating New Hashmap
                    HashMap<String, String>  map = new HashMap<String, String>();

                    //adding each child node to Hashmap key
                    map.put(TAG_ID, id);
                    map.put(TAG_NAME, name);
                    map.put(TAG_LOCATION, location);
                    map.put(TAG_RATING, rating);

                    //adding HashList to ArrayList
                    restaurant_list.add(map);
                }

            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return null;
    }

    protected void onPostExecute(String file_url) {

        //dismiss the dialog
        pDialog.dismiss();


        //Updating UI from the Background Thread
        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                ListAdapter adapter = new SimpleAdapter(
                        SearchAll.this, restaurant_list,
                        R.layout.listview_restaurants, new String[]{
                        TAG_ID, TAG_NAME, TAG_LOCATION, TAG_RATING}, new int[]{
                        R.id.login_id, R.id.restaurant_name,
                        R.id.address, R.id.rating});

                setListAdapter(adapter);


            }
       });

    }
}}

我也在添加我的布局文件,虽然不需要它们但是为了防止有人在制作自定义列表视图时出现问题,这可能会帮助他们解决问题。

布局文件1:activity_search_all.xml

<ListView
    android:id="@android:id/list"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:divider="#bbb"
    android:dividerHeight="3dp"
    android:cacheColorHint="#00000000"
    ></ListView>

</RelativeLayout>

布局文件2:listview_restaurants.xml

<?xml version="1.0" encoding="utf-8"?>

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">


<TextView
    android:id="@+id/login_id"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:visibility="gone" />

<TextView
    android:id="@+id/rating"
    android:layout_width="90dp"
    android:layout_height="70dp"
    android:layout_alignParentTop="true"
    android:layout_gravity="right"

    android:layout_toEndOf="@+id/restaurant_name"
    android:layout_toRightOf="@+id/restaurant_name"
    android:background="#ffb400"
    android:textColor="#444444"
    android:textStyle="bold" />

<TextView
    android:id="@+id/restaurant_name"
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:paddingBottom="15dp"
    android:paddingLeft="2dp"
    android:paddingRight="2dp"
    android:paddingTop="10dp"
    android:textColor="#000000"
    android:textSize="16sp"
    android:textStyle="bold" />

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceSmall"
    android:textColor="#444444"
    android:background="#bbbbbb"
    android:id="@+id/address"
    android:layout_below="@+id/restaurant_name"
    android:layout_toRightOf="@+id/area_id"
    android:layout_toEndOf="@+id/area_id" />


 </RelativeLayout>

更新:1.1 所以通过@Alok的建议,我添加了一个新的&#34; EndlessScrollListener&#34;上课,复制和粘贴。然后在我的&#34; SearchAll&#34;的onCreate方法上上面给出的类,这就是我编码的内容,我知道我需要在 OnloadMore 方法中添加一些内容但是什么?我是新手,请通过编码和评论您的代码来帮助我。

lv.setOnScrollListener(new EndlessScrollListener() {
        @Override
        public void onLoadMore(int page, int totalItemsCount) {

        }
    });

4 个答案:

答案 0 :(得分:0)

如果您对服务器的请求一次获取所有数据,最好在一个请求中获取一定数量的数据,这样当用户向下滚动到加载的ListView的底部时,您可以再次请求在此方法中获取更多数据<登记/>  ListView yourListViw =(ListView)infl.findViewById(R.id.yourListId);

        yourListViw.setOnScrollListener(new OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {

           // check if it is the last item of your list and load more items
                 int currentPosition = yourListViw.getFirstVisiblePosition();
           // call similar async task to load more data with newly added json data
           // set the position of list to latest item 
                yourListViw.setSelectionFromTop(currentPosition + 1, 0);

                }     

             });

答案 1 :(得分:0)

一个常见的应用程序功能是使用AdapterView(例如ListView或GridView),当用户滚动项目(也就是无限滚动)时,它会自动加载更多项目。这是通过在用户超过剩余项目阈值之前触发更多数据请求来完成的。

每个AdapterView都支持绑定OnScrollListener事件,只要用户滚动集合就会触发这些事件。使用这个系统,我们可以通过创建扩展OnScrollListener的自己的类来定义一个支持大多数用例的基本EndlessScrollListener:

public abstract class EndlessScrollListener implements OnScrollListener {
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
// The current offset index of data you have loaded
private int currentPage = 0;
// The total number of items in the dataset after the last load
private int previousTotalItemCount = 0;
// True if we are still waiting for the last set of data to load.
private boolean loading = true;
// Sets the starting page index
private int startingPageIndex = 0;

public EndlessScrollListener() {
}

public EndlessScrollListener(int visibleThreshold) {
    this.visibleThreshold = visibleThreshold;
}

public EndlessScrollListener(int visibleThreshold, int startPage) {
    this.visibleThreshold = visibleThreshold;
    this.startingPageIndex = startPage;
    this.currentPage = startPage;
}

// This happens many times a second during a scroll, so be wary of the code you place here.
// We are given a few useful parameters to help us work out if we need to load some more data,
// but first we check if we are waiting for the previous load to finish.
@Override
public void onScroll(AbsListView view,int firstVisibleItem,int visibleItemCount,int totalItemCount) 
    {
    // If the total item count is zero and the previous isn't, assume the
    // list is invalidated and should be reset back to initial state
    if (totalItemCount < previousTotalItemCount) {
        this.currentPage = this.startingPageIndex;
        this.previousTotalItemCount = totalItemCount;
        if (totalItemCount == 0) { this.loading = true; } 
    }

    // If it’s still loading, we check to see if the dataset count has
    // changed, if so we conclude it has finished loading and update the current page
    // number and total item count.
    if (loading && (totalItemCount > previousTotalItemCount)) {
        loading = false;
        previousTotalItemCount = totalItemCount;
        currentPage++;
    }

    // If it isn’t currently loading, we check to see if we have breached
    // the visibleThreshold and need to reload more data.
    // If we do need to reload some more data, we execute onLoadMore to fetch the data.
    if (!loading && (totalItemCount - visibleItemCount)<=(firstVisibleItem + visibleThreshold)) {
        onLoadMore(currentPage + 1, totalItemCount);
        loading = true;
    }
}

// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int page, int totalItemsCount);

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
    // Don't take any action on changed
}
}

请注意,这是一个抽象类,为了使用它,您必须扩展此基类并定义onLoadMore方法以实际检索新数据。我们现在可以在任何扩展EndlessScrollListener并将其绑定到AdapterView的活动中定义一个匿名类。例如:

public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    // ... the usual 
    ListView lvItems = (ListView) findViewById(R.id.lvItems);
    // Attach the listener to the AdapterView onCreate
    lvItems.setOnScrollListener(new EndlessScrollListener() {
    @Override
    public void onLoadMore(int page, int totalItemsCount) {
            // Triggered only when new data needs to be appended to the list
            // Add whatever code is needed to append new items to your AdapterView
        customLoadMoreDataFromApi(page); 
            // or customLoadMoreDataFromApi(totalItemsCount); 
    }
    });
}

// Append more data into the adapter
public void customLoadMoreDataFromApi(int offset) {
  // This method probably sends out a network request and appends new data items to your adapter. 
  // Use the offset value and add it as a parameter to your API request to retrieve paginated data.
  // Deserialize API response and then construct new objects to append to the adapter
}
}

现在滚动时,项目将自动填充,因为一旦用户越过visibleThreshold,就会触发onLoadMore方法。这种方法同样适用于GridView,并且侦听器可以访问页面和totalItemsCount,以支持基于分页和基于偏移的提取。

如果您遇到问题,请仔细考虑以下建议:

确保在Activity的onCreate方法中设置setOnScrollListener侦听器,而不是稍后,否则您可能会遇到意外问题。

为了让分页系统继续可靠地工作,您应该确保在将新项目附加到列表之前清除项目的适配器(或在清除阵列后通知适配器)。

为了触发此分页系统,请记住,当调用customLoadMoreDataFromApi时,需要将新数据附加到现有数据源。换句话说,在初始&#34;页面&#34;中只清除列表中的项目。随后的&#34;页面&#34;数据应附加到现有数据中。

答案 2 :(得分:0)

您需要限制API抛出的jsonObject的数量,最佳解决方案是在API本身中引入页码请求。然后,您可以收听最后一个滚动位置并在那里显示一个加载更多按钮,当用户点击该按钮时,您可以向服务器请求更多数据并进行相应管理。我可以帮助你在android端进行分页处理,但你必须先安装一个合适的服务器。祝你好运:)

答案 3 :(得分:-1)

更新的答案:由于建议在滚动视图[已删除]中列出视图的建议而拒绝投票

ListView yourListView=(ListView) findViewById(R.id.yourListId);


        yourListView.setOnScrollListener(new OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

                }     

             });