Update ContentLayout

This commit is contained in:
seven332 committed 2015-05-09 00:15:44 +08:00
1 parent 8da847ebc8
commit 755354a8fe
14 files changed
+451 -54

No files matched your search

@@ -23,6 +23,7 @@ import com.hippo.ehviewer.network.EhHttpHelper;
import com.hippo.network.ResponseCodeException;
import com.hippo.util.PriorityThreadFactory;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
@@ -107,7 +108,7 @@ public final class EhClient {
}
public abstract static class OnGetGalleryListListener implements EhClientListener {
public abstract void onSuccess(GalleryInfo[] glArray, int pageNum);
public abstract void onSuccess(List<GalleryInfo> glArray, int pageNum);
}
private void doBgJob(BgJobHelper bjh) {
@@ -182,7 +183,7 @@ public final class EhClient {
ListParser parser = new ListParser();
parser.parse(body, source);
return new Object[]{parser.giArray, parser.pageNum};
return new Object[]{parser.giList, parser.pageNum};
}
private final class GetGalleryListHelper extends SimpleBgJobHelper {
@@ -191,7 +192,7 @@ public final class EhClient {
private String mUrl;
private OnGetGalleryListListener mListener;
private GalleryInfo[] mGlArray;
private List<GalleryInfo> mGlList;
private int mPageNum;
public GetGalleryListHelper(int source, String url, OnGetGalleryListListener listener) {
@@ -204,13 +205,13 @@ public final class EhClient {
@Override
public void doBgJob() throws Exception {
Object[] objs = doGetGalleryList(mSource, mUrl);
mGlArray = (GalleryInfo[]) objs[0];
mGlList = (List<GalleryInfo>) objs[0];
mPageNum = (int) objs[1];
}
@Override
public void doSuccessCallback() {
mListener.onSuccess(mGlArray, mPageNum);
mListener.onSuccess(mGlList, mPageNum);
}
}
@@ -42,7 +42,7 @@ public class ListParser {
*/
public int pageNum;
public GalleryInfo[] giArray;
public List<GalleryInfo> giList;
private void parse(String body) throws EhException {
Pattern p;
@@ -61,7 +61,7 @@ public class ListParser {
}
// Get gallery
List<GalleryInfo> giList = new ArrayList<>(DEFAULT_LIST_SIZE);
giList = new ArrayList<>(DEFAULT_LIST_SIZE);
p = Pattern.compile("<td class=\"itdc\">(?:<a.+?>)?<img.+?alt=\"(.+?)\".+?/>(?:</a>)?</td>" // category
+ "<td.+?>(.+?)</td>" // posted
@@ -113,8 +113,6 @@ public class ListParser {
if (giList.size() == 0) {
throw new EhException(PARSE_MESSAGE);
}
giArray = giList.toArray(new GalleryInfo[giList.size()]);
}
private void parseLofi(String body) throws Exception {
@@ -122,7 +120,7 @@ public class ListParser {
Matcher m;
// Get gallery
List<LofiGalleryInfo> lgiList = new ArrayList<>(DEFAULT_LIST_SIZE);
giList = new ArrayList<>(DEFAULT_LIST_SIZE);
p = Pattern.compile("<td class=\"ii\"><a href=\"(.+?)\">" // detail url
+ "<img src=\"(.+?)\".+?/>" // thumb url
+ ".+?<a class=\"b\" href=\".+?\">(.+?)</a>" // title
@@ -160,10 +158,10 @@ public class ListParser {
lgi.rating = getStartNum(rating);
lgi.generateSLang();
lgiList.add(lgi);
giList.add(lgi);
}
if (lgiList.size() == 0) {
if (giList.size() == 0) {
if (body.contains("No hits found</div>")) {
pageNum = 0;
} else if (body.contains("No more hits found</div>")) {
@@ -226,7 +224,7 @@ public class ListParser {
private void getPostedAndUploader(String raw, String[] pau) throws AssertException {
int index = raw.indexOf(PAU_SPACER);
AssertUtils.assertEqualsEx("Can not parse posted and uploader", index, -1);
AssertUtils.assertNotEqualsEx("Can not parse posted and uploader", index, -1);
pau[0] = raw.substring(0, index);
pau[1] = raw.substring(index + PAU_SPACER.length());
}
@@ -26,17 +26,20 @@ import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;
import com.hippo.animation.SimpleAnimatorListener;
import com.hippo.drawable.AddDeleteDrawable;
import com.hippo.effect.ViewTransition;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.EhClient;
import com.hippo.ehviewer.data.GalleryInfo;
import com.hippo.ehviewer.ui.ContentActivity;
import com.hippo.util.InterpolatorUtils;
import com.hippo.ehviewer.widget.ContentLayout;
import com.hippo.ehviewer.widget.OffsetLayout;
import com.hippo.ehviewer.widget.SearchBar;
@@ -44,12 +47,15 @@ import com.hippo.ehviewer.widget.SearchDatabase;
import com.hippo.ehviewer.widget.SearchLayout;
import com.hippo.scene.Scene;
import com.hippo.util.AssertUtils;
import com.hippo.util.InterpolatorUtils;
import com.hippo.util.Log;
import com.hippo.util.MathUtils;
import com.hippo.util.ViewUtils;
import com.hippo.widget.FabLayout;
import com.hippo.widget.FloatingActionButton;
import java.util.List;
// TODO disable click action when animating
public class GalleryListScene extends Scene implements SearchBar.Helper,
View.OnClickListener, FabLayout.OnCancelListener,
@@ -80,6 +86,8 @@ public class GalleryListScene extends Scene implements SearchBar.Helper,
private ViewTransition mViewTransition;
private GalleryListHelper mGalleryListHelper;
private int mSearchBarOriginalTop;
private int mSearchBarOriginalBottom;
private ValueAnimator mSearchBarMoveAnimator;
@@ -131,6 +139,11 @@ public class GalleryListScene extends Scene implements SearchBar.Helper,
});
mSearchLayout.setHelper(this);
// Content Layout
mGalleryListHelper = new GalleryListHelper();
mContentLayout.setHelper(mGalleryListHelper);
mContentLayout.showContent();
// Fab Layout
mFabLayout.setOnCancelListener(this);
@@ -147,7 +160,7 @@ public class GalleryListScene extends Scene implements SearchBar.Helper,
mCornerFab.setDrawable(mAddDeleteDrawable);
// TEST
mContentLayout.showText("四姑拉斯基");
mGalleryListHelper.refresh();
}
@Override
@@ -178,6 +191,8 @@ public class GalleryListScene extends Scene implements SearchBar.Helper,
@Override
protected void onGetFitPaddingBottom(int b) {
// Content Layout
mContentLayout.setFitPaddingBottom(b);
// Search Layout
mSearchLayout.setFitPaddingBottom(b);
// Corner Fab
@@ -470,4 +485,45 @@ public class GalleryListScene extends Scene implements SearchBar.Helper,
(mState == STATE_SEARCH && view == mSearchLayout);
}
}
private class GalleryHolder extends RecyclerView.ViewHolder {
public GalleryHolder(View itemView) {
super(itemView);
}
}
private class GalleryListHelper extends ContentLayout.ContentHelper<GalleryInfo, GalleryHolder> {
@Override
protected StaggeredGridLayoutManager generateLayoutManager() {
return new StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.VERTICAL);
}
@Override
protected void getPageData(final int taskId, int page) {
EhClient.getInstance().getGalleryList(EhClient.SOURCE_LOFI, "http://lofi.e-hentai.org/", new EhClient.OnGetGalleryListListener() {
@Override
public void onSuccess(List<GalleryInfo> glArray, int pageNum) {
setPageSize(pageNum);
onGetPageData(taskId, glArray);
}
@Override
public void onFailure(Exception e) {
Log.d("onFailure", e);
}
});
}
@Override
public GalleryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new GalleryHolder(new TextView(mActivity));
}
@Override
public void onBindViewHolder(GalleryHolder holder, int position) {
((TextView) holder.itemView).setText(getDataAt(position).title);
}
}
}
@@ -28,7 +28,9 @@ import android.widget.TextView;
import com.hippo.ehviewer.R;
import com.hippo.util.IntIdGenerator;
import com.hippo.util.Log;
import com.hippo.util.ViewUtils;
import com.hippo.widget.Snackbar;
import com.hippo.widget.recyclerview.EasyRecyclerView;
import com.hippo.widget.refreshlayout.RefreshLayout;
@@ -43,11 +45,14 @@ public class ContentLayout extends FrameLayout {
private EasyRecyclerView mRecyclerView;
private View mImageView;
private TextView mTextView;
private Snackbar mSnackbar;
private StaggeredGridLayoutManager mLayoutManager;
private ContentHelper mHelper;
private int mSnackbarOriginBottom;
public ContentLayout(Context context) {
super(context);
init(context);
@@ -69,12 +74,19 @@ public class ContentLayout extends FrameLayout {
mProgressBar = (ProgressBar) getChildAt(0);
mItView = (ViewGroup) getChildAt(1);
mRefreshLayout = (RefreshLayout) getChildAt(2);
mSnackbar = (Snackbar) getChildAt(3);
mRecyclerView = (EasyRecyclerView) mRefreshLayout.getChildAt(1);
mImageView = mItView.getChildAt(0);
mTextView = (TextView) mItView.getChildAt(1);
mLayoutManager = new StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.VERTICAL); // TODO
mRecyclerView.setLayoutManager(mLayoutManager);
// Snackbar
mSnackbarOriginBottom = mSnackbar.getPaddingBottom();
mSnackbar.setAction(context.getString(R.string.retry), new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
public EasyRecyclerView getRecyclerView() {
@@ -83,12 +95,20 @@ public class ContentLayout extends FrameLayout {
public void setHelper(ContentHelper helper) {
mHelper = helper;
helper.init(mRecyclerView, mLayoutManager);
mLayoutManager = helper.generateLayoutManager();
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(helper);
helper.init(mRefreshLayout, mRecyclerView, mLayoutManager);
mRefreshLayout.setOnHeaderRefreshListener(helper);
mRefreshLayout.setOnFooterRefreshListener(helper);
mRecyclerView.addOnScrollListener(helper.getOnScrollListener());
}
public void setFitPaddingBottom(int fitPaddingBottom) {
// Snackbar
mSnackbar.setPadding(mSnackbar.getPaddingLeft(), mSnackbar.getPaddingTop(), mSnackbar.getPaddingRight(), mSnackbarOriginBottom + fitPaddingBottom);
}
public void showProgressBar() {
ViewUtils.setVisibility(mProgressBar, View.VISIBLE);
ViewUtils.setVisibility(mItView, View.GONE);
@@ -103,11 +123,25 @@ public class ContentLayout extends FrameLayout {
mTextView.setText(text);
}
private abstract static class ContentHelper<E, VH extends RecyclerView.ViewHolder>
public void showContent() {
ViewUtils.setVisibility(mProgressBar, View.GONE);
ViewUtils.setVisibility(mItView, View.GONE);
ViewUtils.setVisibility(mRefreshLayout, View.VISIBLE);
}
public abstract static class ContentHelper<E, VH extends RecyclerView.ViewHolder>
extends EasyRecyclerView.Adapter<VH>
implements RefreshLayout.OnHeaderRefreshListener,
RefreshLayout.OnFooterRefreshListener {
private static final int TYPE_REFRESH = 0;
private static final int TYPE_PRE_PAGE = 1;
private static final int TYPE_PRE_PAGE_KEEP_POS = 2;
private static final int TYPE_NEXT_PAGE = 3;
private static final int TYPE_NEXT_PAGE_KEEP_POS = 4;
private static final int TYPE_SOMEWHERE = 5;
private RefreshLayout mRefreshLayout;
private RecyclerView mRecyclerView;
private StaggeredGridLayoutManager mLayoutManager;
@@ -121,96 +155,232 @@ public class ContentLayout extends FrameLayout {
/**
* Store data
*/
private List<E> mList;
private List<E> mData;
private IntIdGenerator mIdGenerator;
/**
* First index index of current page
*/
private int mFirstIndex;
/**
* Last index index of current page
*/
private int mLastIndex;
/**
* Current page index
*/
private int mCurrentPage;
/**
* First shown page index
*/
private int mFirstPage;
/**
* Last shown page index
* Last shown page index + 1
*/
private int mLastPage;
/**
* The number of page in sum
* First index index of current page
*/
private int mPageNum;
private int mFirstIndex;
/**
* Last index index of current page + 1
*/
private int mLastIndex;
/**
* The size of page
*/
private int mPageSize;
private int mCurrentPage;
private int mPageVolume;
private int mCurrentTaskId;
private int mCurrentTaskType;
private int mCurrentTaskPage;
private ContentHelper() {
mList = new ArrayList<>();
public ContentHelper() {
mData = new ArrayList<>();
mIdGenerator = IntIdGenerator.create();
}
private void init(RecyclerView recyclerView,
private void init(RefreshLayout refreshLayout, RecyclerView recyclerView,
StaggeredGridLayoutManager layoutManager) {
mRefreshLayout = refreshLayout;
mRecyclerView = recyclerView;
mLayoutManager = layoutManager;
}
RecyclerView.OnScrollListener getOnScrollListener() {
private RecyclerView.OnScrollListener getOnScrollListener() {
return mOnScrollListener;
}
protected abstract StaggeredGridLayoutManager generateLayoutManager();
/**
*
* @param location
* @return
* @throws IndexOutOfBoundsException
* if {@code location < 0 || location >= size()}
*/
public E getDataAt(int location) {
return mList.get(location);
return mData.get(location);
}
public abstract E[] getPageData(int page);
/**
* Call {@link #onGetPageData(int, List)} when get data
*
* @param taskId task id
* @param page the page to get
*/
protected abstract void getPageData(int taskId, int page);
public void onGetPageData(int mTaskId, int page, E[] data) {
public void setPageSize(int pageSize) {
mPageSize = pageSize;
}
public void onGetPageData(int taskId, List<E> data) {
int pageVolume = data.size();
mPageVolume = pageVolume;
if (mCurrentTaskId == taskId) {
switch (mCurrentTaskType) {
case TYPE_REFRESH:
mFirstPage = 0;
mLastPage = 1;
mCurrentPage = 0;
mFirstIndex = 0;
mLastIndex = pageVolume;
mData.clear();
mData.addAll(data);
notifyDataSetChanged();
mRecyclerView.stopScroll();
mLayoutManager.scrollToPositionWithOffset(0, 0);
break;
case TYPE_PRE_PAGE:
case TYPE_PRE_PAGE_KEEP_POS:
mData.addAll(0, data);
notifyItemRangeInserted(0, pageVolume);
mFirstPage--;
if (mCurrentTaskType == TYPE_PRE_PAGE_KEEP_POS) {
mFirstIndex += pageVolume;
mLastIndex += pageVolume;
// TODO
} else {
mCurrentPage = mFirstPage;
mFirstIndex = 0;
mLastIndex = pageVolume;
mRecyclerView.stopScroll();
mLayoutManager.scrollToPositionWithOffset(0, 0);
}
break;
case TYPE_NEXT_PAGE:
case TYPE_NEXT_PAGE_KEEP_POS:
int oldDataSize = mData.size();
mData.addAll(data);
notifyItemRangeInserted(oldDataSize, pageVolume);
mLastPage++;
if (mCurrentTaskType != TYPE_NEXT_PAGE_KEEP_POS) {
mCurrentPage = mLastPage - 1;
mFirstIndex = mData.size() - pageVolume;
mLastIndex = mData.size();
mRecyclerView.stopScroll();
mLayoutManager.scrollToPositionWithOffset(mFirstIndex, 0);
}
break;
case TYPE_SOMEWHERE:
mData.clear();
mData.addAll(data);
notifyDataSetChanged();
mFirstPage = mCurrentTaskPage;
mLastPage = mCurrentTaskPage + 1;
mCurrentPage = mCurrentTaskPage;
mFirstIndex = 0;
mLastIndex = pageVolume;
mRecyclerView.stopScroll();
mLayoutManager.scrollToPositionWithOffset(0, 0);
break;
}
}
mRefreshLayout.setHeaderRefreshing(false);
mRefreshLayout.setFooterRefreshing(false);
}
@Override
public boolean onFooterRefresh() {
return false;
if (mLastPage >= mPageSize) {
return false;
} else {
mCurrentTaskId = mIdGenerator.nextId();
mCurrentTaskType = TYPE_NEXT_PAGE_KEEP_POS;
mCurrentTaskPage = mLastPage;
getPageData(mCurrentTaskId, mCurrentTaskPage);
return true;
}
}
@Override
public void onHeaderRefresh() {
if (mFirstPage > 0) {
mCurrentTaskId = mIdGenerator.nextId();
mCurrentTaskType = TYPE_PRE_PAGE_KEEP_POS;
mCurrentTaskPage = mFirstPage - 1;
getPageData(mCurrentTaskId, mCurrentTaskPage);
} else {
doRefresh();
}
}
private void doRefresh() {
Log.d("doRefresh");
mCurrentTaskId = mIdGenerator.nextId();
mCurrentTaskType = TYPE_REFRESH;
mCurrentTaskPage = 0;
getPageData(mCurrentTaskId, mCurrentTaskPage);
}
public void refresh() {
mRefreshLayout.setHeaderRefreshing(true);
doRefresh();
}
public void goTo(int page) {
if (page < 0 || page >= mPageNum) {
throw new IndexOutOfBoundsException("Page number is " + mPageNum + ", page is " + page);
if (page < 0 || page >= mPageSize) {
throw new IndexOutOfBoundsException("Page size is " + mPageSize + ", page is " + page);
} else if (page >= mFirstPage && page < mLastPage) {
mCurrentPage = page;
mFirstIndex = mPageVolume * (page - mFirstPage);
mLastIndex = mFirstIndex + mPageVolume;
int position = mFirstIndex;
mRecyclerView.stopScroll();
mLayoutManager.scrollToPositionWithOffset(position, 0);
} else if (page == mFirstPage - 1) {
mCurrentTaskId = mIdGenerator.nextId();
mCurrentTaskType = TYPE_PRE_PAGE;
mCurrentTaskPage = page;
getPageData(mCurrentTaskId, mCurrentTaskPage);
} else if (page == mLastPage) {
mCurrentTaskId = mIdGenerator.nextId();
mCurrentTaskType = TYPE_NEXT_PAGE;
mCurrentTaskPage = page;
getPageData(mCurrentTaskId, mCurrentTaskPage);
} else {
mCurrentTaskId = mIdGenerator.nextId();
mCurrentTaskType = TYPE_SOMEWHERE;
mCurrentTaskPage = page;
getPageData(mCurrentTaskId, mCurrentTaskPage);
}
}
if (page > mFirstIndex) {
}
public void reload() {
notifyDataSetChanged();
mCurrentPage = mFirstPage;
mFirstIndex = 0;
mLastIndex = mPageVolume;
mRecyclerView.stopScroll();
mLayoutManager.scrollToPositionWithOffset(0, 0);
}
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
}
@Override
public int getItemCount() {
return mData.size();
}
}
}
@@ -35,6 +35,12 @@ public class AssertUtils {
}
}
public static void assertNotEquals(String message, int expected, int actual) {
if (expected == actual) {
throw new AssertError(message);
}
}
public static void assertInstanceof(String message, Object obj, Class clazz) {
if (!clazz.isInstance(obj)) {
throw new AssertError(message);
@@ -62,6 +68,13 @@ public class AssertUtils {
}
}
public static void assertNotEqualsEx(String message, int expected, int actual)
throws AssertException {
if (expected == actual) {
throw new AssertException(message);
}
}
public static void assertInstanceofEx(String message, Object obj, Class clazz)
throws AssertException {
if (!clazz.isInstance(obj)) {
@@ -0,0 +1,89 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.widget;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.hippo.ehviewer.R;
import com.hippo.util.ViewUtils;
public class Snackbar extends LinearLayout {
private TextView mMessageTxt;
private View mSpace;
private TextView mActionTxt;
public Snackbar(Context context) {
super(context);
init(context, null);
}
public Snackbar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public Snackbar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
Resources resources = context.getResources();
setBackgroundColor(resources.getColor(R.color.snackbar_background));
setPadding(resources.getDimensionPixelOffset(R.dimen.snackbar_padding_left),
resources.getDimensionPixelOffset(R.dimen.snackbar_padding_top),
resources.getDimensionPixelOffset(R.dimen.snackbar_padding_right),
resources.getDimensionPixelOffset(R.dimen.snackbar_padding_bottom));
setOrientation(LinearLayout.HORIZONTAL);
LayoutInflater.from(context).inflate(R.layout.widget_snackbar, this);
mMessageTxt = (TextView) getChildAt(0);
mSpace = getChildAt(1);
mActionTxt = (TextView) getChildAt(2);
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.Snackbar);
setMessage(a.getString(R.styleable.Snackbar_message));
a.recycle();
}
public void setMessage(String message) {
mMessageTxt.setText(message);
}
public void setAction(String actionText, View.OnClickListener action) {
if (TextUtils.isEmpty(actionText)) {
mActionTxt.setOnClickListener(null);
mActionTxt.setClickable(false);
ViewUtils.setVisibility(mSpace, View.GONE);
ViewUtils.setVisibility(mActionTxt, View.GONE);
} else {
mActionTxt.setText(actionText);
mActionTxt.setOnClickListener(action);
ViewUtils.setVisibility(mSpace, View.VISIBLE);
ViewUtils.setVisibility(mActionTxt, View.VISIBLE);
}
}
}
@@ -1064,10 +1064,10 @@ public class RefreshLayout extends ViewGroup {
* triggers a refresh should implement this interface.
*/
public interface OnHeaderRefreshListener {
public void onHeaderRefresh();
void onHeaderRefresh();
}
public interface OnFooterRefreshListener {
public boolean onFooterRefresh();
boolean onFooterRefresh();
}
}