automatically load new headlines when scrolled to the bottom

This commit is contained in:
Andrew Dolgov 2011-11-30 14:45:23 +03:00
parent 918235592a
commit 24e092d190
4 changed files with 108 additions and 32 deletions

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/headlines_row_loadmore"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?headlineNormalBackground"
android:gravity="center_vertical"
android:orientation="horizontal" >
<LinearLayout
android:id="@+id/loadmore_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_weight="1"
android:orientation="horizontal"
android:padding="10dp" >
<ProgressBar
android:id="@+id/loadmore_progress"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:paddingLeft="6dp"
android:id="@+id/loadmore_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Loading, please wait..." />
</LinearLayout>
</LinearLayout>

View File

@ -24,6 +24,13 @@ public class Article implements Parcelable {
readFromParcel(in);
}
public Article(int id) {
this.id = id;
this.title = "";
this.link = "";
this.tags = new ArrayList<String>();
}
@Override
public int describeContents() {
return 0;

View File

@ -23,6 +23,8 @@ import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
@ -37,13 +39,18 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
public class HeadlinesFragment extends Fragment implements OnItemClickListener {
public class HeadlinesFragment extends Fragment implements OnItemClickListener, OnScrollListener {
public static enum ArticlesSelection { ALL, NONE, UNREAD };
public static final int HEADLINES_REQUEST_SIZE = 30;
public static final int HEADLINES_BUFFER_MAX = 500;
private final String TAG = this.getClass().getSimpleName();
private Feed m_feed;
private int m_activeArticleId;
private boolean m_refreshInProgress = false;
private boolean m_canLoadMore = false;
private ArticleListAdapter m_adapter;
private ArticleList m_articles = new ArticleList();
@ -81,6 +88,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener {
m_articles = savedInstanceState.getParcelable("articles");
m_activeArticleId = savedInstanceState.getInt("activeArticleId");
m_selectedArticles = savedInstanceState.getParcelable("selectedArticles");
m_canLoadMore = savedInstanceState.getBoolean("canLoadMore");
}
View view = inflater.inflate(R.layout.headlines_fragment, container, false);
@ -89,6 +97,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener {
m_adapter = new ArticleListAdapter(getActivity(), R.layout.headlines_row, (ArrayList<Article>)m_articles);
list.setAdapter(m_adapter);
list.setOnItemClickListener(this);
list.setOnScrollListener(this);
registerForContextMenu(list);
Log.d(TAG, "onCreateView, feed=" + m_feed);
@ -114,15 +123,19 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener {
if (list != null) {
Article article = (Article)list.getItemAtPosition(position);
m_articleOps.openArticle(article, 0);
if (article.id >= 0) {
m_articleOps.openArticle(article, 0);
m_activeArticleId = article.id;
m_adapter.notifyDataSetChanged();
m_activeArticleId = article.id;
m_adapter.notifyDataSetChanged();
}
}
}
@SuppressWarnings({ "unchecked", "serial" })
public void refresh(boolean append) {
m_refreshInProgress = true;
HeadlinesRequest req = new HeadlinesRequest(getActivity().getApplicationContext());
final String sessionId = ((MainActivity)getActivity()).getSessionId();
@ -136,21 +149,21 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener {
}
if (skip == 0) skip = m_articles.size();
} else {
setLoadingStatus(R.string.blank, true);
}
final int fskip = skip;
req.setOffset(skip);
setLoadingStatus(R.string.blank, true);
HashMap<String,String> map = new HashMap<String,String>() {
{
put("op", "getHeadlines");
put("sid", sessionId);
put("feed_id", String.valueOf(m_feed.id));
put("show_content", "true");
put("limit", String.valueOf(30));
put("limit", String.valueOf(HEADLINES_REQUEST_SIZE));
put("offset", String.valueOf(0));
put("view_mode", showUnread ? "adaptive" : "all_articles");
put("skip", String.valueOf(fskip));
@ -170,6 +183,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener {
out.putParcelable("articles", m_articles);
out.putInt("activeArticleId", m_activeArticleId);
out.putParcelable("selectedArticles", m_selectedArticles);
out.putBoolean("canLoadMore", m_canLoadMore);
}
public void setLoadingStatus(int status, boolean showProgress) {
@ -203,31 +217,43 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener {
Type listType = new TypeToken<List<Article>>() {}.getType();
final List<Article> articles = new Gson().fromJson(content, listType);
while (m_articles.size() > HEADLINES_BUFFER_MAX)
m_articles.remove(0);
if (m_offset == 0)
m_articles.clear();
else
m_articles.remove(m_articles.size()-1); // remove previous placeholder
int last_position = m_articles.size();
for (Article f : articles)
m_articles.add(f);
if (articles.size() == HEADLINES_REQUEST_SIZE) {
Article placeholder = new Article(-1);
m_articles.add(placeholder);
m_canLoadMore = true;
} else {
m_canLoadMore = false;
}
m_adapter.notifyDataSetChanged();
ListView list = (ListView)getView().findViewById(R.id.headlines);
/* ListView list = (ListView)getView().findViewById(R.id.headlines);
if (list != null && m_offset != 0 && articles.size() > 0) {
list.setSelection(last_position-1);
}
MainActivity activity = (MainActivity)getActivity();
activity.setCanLoadMore(articles.size() >= 30);
activity.initMainMenu();
} */
if (m_articles.size() == 0)
setLoadingStatus(R.string.no_headlines_to_display, false);
else
setLoadingStatus(R.string.blank, false);
m_refreshInProgress = false;
return;
}
@ -242,6 +268,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener {
} else {
setLoadingStatus(getErrorMessage(), false);
}
m_refreshInProgress = false;
}
public void setOffset(int skip) {
@ -255,8 +282,9 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener {
public static final int VIEW_NORMAL = 0;
public static final int VIEW_UNREAD = 1;
public static final int VIEW_SELECTED = 2;
public static final int VIEW_LOADMORE = 3;
public static final int VIEW_COUNT = VIEW_SELECTED+1;
public static final int VIEW_COUNT = VIEW_LOADMORE+1;
public ArticleListAdapter(Context context, int textViewResourceId, ArrayList<Article> items) {
super(context, textViewResourceId, items);
@ -271,7 +299,9 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener {
public int getItemViewType(int position) {
Article a = items.get(position);
if (a.id == m_activeArticleId) {
if (a.id == -1) {
return VIEW_LOADMORE;
} else if (a.id == m_activeArticleId) {
return VIEW_SELECTED;
} else if (a.unread) {
return VIEW_UNREAD;
@ -291,6 +321,9 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener {
int layoutId = R.layout.headlines_row;
switch (getItemViewType(position)) {
case VIEW_LOADMORE:
layoutId = R.layout.headlines_row_loadmore;
break;
case VIEW_UNREAD:
layoutId = R.layout.headlines_row_unread;
break;
@ -439,4 +472,16 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener {
return tmp;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (!m_refreshInProgress && m_canLoadMore && firstVisibleItem + visibleItemCount == m_articles.size()) {
refresh(true);
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
}

View File

@ -49,7 +49,7 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
private boolean m_smallScreenMode;
private boolean m_unreadOnly = true;
private boolean m_unreadArticlesOnly = true;
private boolean m_canLoadMore = true;
//private boolean m_canLoadMore = true;
private boolean m_compatMode = false;
private boolean m_enableCats = false;
private int m_apiLevel = 0;
@ -302,7 +302,7 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
m_activeFeed = savedInstanceState.getParcelable("activeFeed");
m_selectedArticle = savedInstanceState.getParcelable("selectedArticle");
m_unreadArticlesOnly = savedInstanceState.getBoolean("unreadArticlesOnly");
m_canLoadMore = savedInstanceState.getBoolean("canLoadMore");
//m_canLoadMore = savedInstanceState.getBoolean("canLoadMore");
m_activeCategory = savedInstanceState.getParcelable("activeCategory");
m_apiLevel = savedInstanceState.getInt("apiLevel");
}
@ -397,7 +397,7 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
out.putParcelable("activeFeed", m_activeFeed);
out.putParcelable("selectedArticle", m_selectedArticle);
out.putBoolean("unreadArticlesOnly", m_unreadArticlesOnly);
out.putBoolean("canLoadMore", m_canLoadMore);
//out.putBoolean("canLoadMore", m_canLoadMore);
out.putParcelable("activeCategory", m_activeCategory);
out.putInt("apiLevel", m_apiLevel);
}
@ -581,7 +581,6 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
dialog = builder.create();
dialog.show();
}
return true;
case R.id.headlines_mark_as_read:
if (hf != null) {
@ -590,14 +589,7 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
for (Article a : articles)
a.unread = false;
ApiRequest req = new ApiRequest(getApplicationContext()) {
@Override
protected void onPostExecute(JsonElement result) {
if (result != null) {
viewFeed(m_activeFeed, true);
}
}
};
ApiRequest req = new ApiRequest(getApplicationContext());
final String articleIds = articlesToIdString(articles);
@ -613,7 +605,6 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
};
req.execute(map);
}
return true;
case R.id.share_article:
@ -736,9 +727,9 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
}
public void setCanLoadMore(boolean canLoadMore) {
/* public void setCanLoadMore(boolean canLoadMore) {
m_canLoadMore = canLoadMore;
}
} */
public void initMainMenu() {
if (m_menu != null) {