Add SceneGalleryPreviews
This commit is contained in:
1 parent
cac0aedc42
commit
6d58477f37
24 files changed
+1282
-33
No files matched your search
@@ -27,6 +27,7 @@ import com.hippo.drawable.ImageWrapper;
|
||||
import com.hippo.ehviewer.client.EhClient;
|
||||
import com.hippo.ehviewer.client.EhCookieStore;
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail;
|
||||
import com.hippo.ehviewer.client.data.LargePreviewSet;
|
||||
import com.hippo.network.StatusCodeException;
|
||||
import com.hippo.okhttp.CookieDB;
|
||||
import com.hippo.scene.SceneApplication;
|
||||
@@ -52,6 +53,8 @@ public class EhApplication extends SceneApplication {
|
||||
private ImageWrapperHelper mImageWrapperHelper;
|
||||
private Conaco<ImageWrapper> mConaco;
|
||||
private LruCacheEx<Integer, GalleryDetail> mGalleryDetailCache;
|
||||
private LruCacheEx<String, LargePreviewSet> mLargePreviewSetCache;
|
||||
private LruCacheEx<Integer, Integer> mPreviewPagesCache;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@@ -179,4 +182,34 @@ public class EhApplication extends SceneApplication {
|
||||
}
|
||||
return application.mGalleryDetailCache;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static LruCacheEx<String, LargePreviewSet> getLargePreviewSetCache(@NonNull Context context) {
|
||||
EhApplication application = ((EhApplication) context.getApplicationContext());
|
||||
if (application.mLargePreviewSetCache == null) {
|
||||
// Max size 50, 3 min timeout
|
||||
application.mLargePreviewSetCache = new LruCacheEx<>(50, 3 * 60 * 1000, new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(String lhs, String rhs) {
|
||||
return lhs.compareTo(rhs);
|
||||
}
|
||||
});
|
||||
}
|
||||
return application.mLargePreviewSetCache;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static LruCacheEx<Integer, Integer> getPreviewPagesCache(@NonNull Context context) {
|
||||
EhApplication application = ((EhApplication) context.getApplicationContext());
|
||||
if (application.mPreviewPagesCache == null) {
|
||||
// Max size 50, 3 min timeout
|
||||
application.mPreviewPagesCache = new LruCacheEx<>(50, 3 * 60 * 1000, new Comparator<Integer>() {
|
||||
@Override
|
||||
public int compare(Integer lhs, Integer rhs) {
|
||||
return lhs - rhs;
|
||||
}
|
||||
});
|
||||
}
|
||||
return application.mPreviewPagesCache;
|
||||
}
|
||||
}
|
||||
@@ -22,20 +22,18 @@ public class EhCacheKeyFactory {
|
||||
return "thumb:" + gid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index start from 0
|
||||
*/
|
||||
public static String getNormalPreviewKey(int gid, int index) {
|
||||
return "preview:normal:" + gid + ":" + index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index start from 0
|
||||
*/
|
||||
public static String getLargePreviewKey(int gid, int index) {
|
||||
return "preview:large:" + gid + ":" + index;
|
||||
}
|
||||
|
||||
public static String getLargePreviewSetKey(int gid, int index) {
|
||||
return "large_preview_set:" + gid + ":" + index;
|
||||
}
|
||||
|
||||
public static String getImageKey(int gid, int index) {
|
||||
return "image:" + gid + ":" + index;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ public class EhClient {
|
||||
public static final int METHOD_SIGN_IN = 0;
|
||||
public static final int METHOD_GET_GALLERY_LIST = 1;
|
||||
public static final int METHOD_GET_GALLERY_DETAIL = 2;
|
||||
public static final int METHOD_GET_LARGE_PREVIEW_SET = 3;
|
||||
|
||||
private final ThreadPoolExecutor mRequestThreadPool;
|
||||
private final OkHttpClient mOkHttpClient;
|
||||
@@ -138,6 +139,16 @@ public class EhClient {
|
||||
}
|
||||
}
|
||||
|
||||
private Object getLargePreviewSet(EhConfig ehConfig, Object... params) throws Exception {
|
||||
Call call = EhEngine.prepareGetLargePreviewSet(mOkHttpClient, ehConfig, (String) params[0]);
|
||||
if (!mStop) {
|
||||
mCall = call;
|
||||
return EhEngine.doGetLargePreviewSet(call);
|
||||
} else {
|
||||
throw new CancelledException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackground(Object... params) {
|
||||
try {
|
||||
@@ -148,6 +159,8 @@ public class EhClient {
|
||||
return getGalleryList(mEhConfig, params);
|
||||
case METHOD_GET_GALLERY_DETAIL:
|
||||
return getGalleryDetail(mEhConfig, params);
|
||||
case METHOD_GET_LARGE_PREVIEW_SET:
|
||||
return getLargePreviewSet(mEhConfig, params);
|
||||
default:
|
||||
return new IllegalStateException("Can't detect method " + mMethod);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ package com.hippo.ehviewer.client;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail;
|
||||
import com.hippo.ehviewer.client.data.LargePreviewSet;
|
||||
import com.hippo.ehviewer.client.exception.CancelledException;
|
||||
import com.hippo.ehviewer.client.exception.EhException;
|
||||
import com.hippo.ehviewer.client.parser.GalleryDetailParser;
|
||||
@@ -142,4 +144,27 @@ public class EhEngine {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static Call prepareGetLargePreviewSet(OkHttpClient okHttpClient,
|
||||
EhConfig ehConfig, String url) {
|
||||
Log.d(TAG, url);
|
||||
Request request = new EhRequestBuilder(url, ehConfig).build();
|
||||
return okHttpClient.newCall(request);
|
||||
}
|
||||
|
||||
public static Pair<LargePreviewSet, Integer> doGetLargePreviewSet(Call call) throws Exception {
|
||||
String body = null;
|
||||
Headers headers = null;
|
||||
int code = -1;
|
||||
try {
|
||||
Response response = call.execute();
|
||||
code = response.code();
|
||||
headers = response.headers();
|
||||
body = response.body().string();
|
||||
return Pair.create(GalleryDetailParser.parseLargePreview(body),GalleryDetailParser.parsePreviewPages(body));
|
||||
} catch (Exception e) {
|
||||
throwException(call, code, headers, body, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,9 +33,10 @@ public class EhUrl {
|
||||
|
||||
public static String getGalleryDetailUrl(int gid, String token, int index) {
|
||||
StringBuilder sb = new StringBuilder(HOST_EX);
|
||||
sb.append("g/").append(gid).append('/').append(token);
|
||||
// TODO set hc 1 or 0
|
||||
sb.append("g/").append(gid).append('/').append(token).append("/?hc=1");
|
||||
if (index != 0) {
|
||||
sb.append("/?p=").append(index);
|
||||
sb.append("&p=").append(index);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.ehviewer.client.data;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class GalleryPreview implements Parcelable {
|
||||
|
||||
public String imageUrl;
|
||||
public String pageUrl;
|
||||
public int index;
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(this.imageUrl);
|
||||
dest.writeString(this.pageUrl);
|
||||
dest.writeInt(this.index);
|
||||
}
|
||||
|
||||
public GalleryPreview() {
|
||||
}
|
||||
|
||||
protected GalleryPreview(Parcel in) {
|
||||
this.imageUrl = in.readString();
|
||||
this.pageUrl = in.readString();
|
||||
this.index = in.readInt();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<GalleryPreview> CREATOR = new Parcelable.Creator<GalleryPreview>() {
|
||||
public GalleryPreview createFromParcel(Parcel source) {
|
||||
return new GalleryPreview(source);
|
||||
}
|
||||
|
||||
public GalleryPreview[] newArray(int size) {
|
||||
return new GalleryPreview[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -252,7 +252,7 @@ public class GalleryDetailParser {
|
||||
*/
|
||||
}
|
||||
|
||||
private static LargePreviewSet parseLargePreview(String body) {
|
||||
public static LargePreviewSet parseLargePreview(String body) {
|
||||
Matcher m = LARGE_PREVIEW_PATTERN.matcher(body);
|
||||
LargePreviewSet largePreviewSet = new LargePreviewSet();
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
@@ -67,11 +68,22 @@ import com.hippo.widget.LoadImageView;
|
||||
import com.hippo.widget.SimpleGridLayout;
|
||||
import com.hippo.widget.SimpleImageView;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Locale;
|
||||
|
||||
// TODO Update drawer checked item
|
||||
public class GalleryDetailScene extends BaseScene implements View.OnClickListener {
|
||||
|
||||
@IntDef({STATE_NORMAL, STATE_REFRESH, STATE_REFRESH_HEADER, STATE_FAILED})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
private @interface State {}
|
||||
|
||||
private static final int STATE_NORMAL = 0;
|
||||
private static final int STATE_REFRESH = 1;
|
||||
private static final int STATE_REFRESH_HEADER = 2;
|
||||
private static final int STATE_FAILED = 3;
|
||||
|
||||
public final static String KEY_ACTION = "action";
|
||||
public static final String ACTION_GALLERY_INFO = "action_gallery_info";
|
||||
public static final String ACTION_GID_TOKEN = "action_gid_token";
|
||||
@@ -144,6 +156,9 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
private GalleryDetail mGalleryDetail;
|
||||
private int mRequestId;
|
||||
|
||||
@State
|
||||
private int mState;
|
||||
|
||||
private void handleArgs(Bundle args) {
|
||||
if (args == null) {
|
||||
return;
|
||||
@@ -191,6 +206,18 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
}
|
||||
}
|
||||
|
||||
private String getToken() {
|
||||
if (mGalleryDetail != null) {
|
||||
return mGalleryDetail.token;
|
||||
} else if (mGalleryInfo != null) {
|
||||
return mGalleryInfo.token;
|
||||
} else if (ACTION_GID_TOKEN.equals(mAction)) {
|
||||
return mToken;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getUploader() {
|
||||
if (mGalleryDetail != null) {
|
||||
return mGalleryDetail.uploader;
|
||||
@@ -324,6 +351,8 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
|
||||
mComments = (LinearLayout) belowHeader.findViewById(R.id.comments);
|
||||
mCommentsText = (TextView) mComments.findViewById(R.id.comments_text);
|
||||
RippleSalon.addRipple(mComments, false);
|
||||
mComments.setOnClickListener(this);
|
||||
|
||||
mPreviews = belowHeader.findViewById(R.id.previews);
|
||||
mGridLayout = (SimpleGridLayout) mPreviews.findViewById(R.id.grid_layout);
|
||||
@@ -337,12 +366,17 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
if (mGalleryDetail != null) {
|
||||
bindViewSecond();
|
||||
setTransitionName();
|
||||
mState = STATE_NORMAL;
|
||||
} else if (mGalleryInfo != null) {
|
||||
mState = STATE_REFRESH_HEADER;
|
||||
bindViewFirst();
|
||||
setTransitionName();
|
||||
} else {
|
||||
mState = STATE_REFRESH;
|
||||
}
|
||||
} else {
|
||||
mFailedText.setText(R.string.cannot_find_gallery);
|
||||
mState = STATE_FAILED;
|
||||
mFailedText.setText(R.string.error_cannot_find_gallery);
|
||||
}
|
||||
adjustViewVisibility(false);
|
||||
|
||||
@@ -449,22 +483,27 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
}
|
||||
|
||||
private void adjustViewVisibility(boolean animation) {
|
||||
if (mGalleryDetail != null) {
|
||||
// Show mMainView
|
||||
mViewTransition.showView(0, animation);
|
||||
// Show mBelowHeader
|
||||
mViewTransition2.showView(0, animation);
|
||||
} else if (mGalleryInfo != null) {
|
||||
// Show mMainView
|
||||
mViewTransition.showView(0, animation);
|
||||
// Show mProgress
|
||||
mViewTransition2.showView(1, animation);
|
||||
} else if (TextUtils.isEmpty(mFailedText.getText())) {
|
||||
// Show mProgressView
|
||||
mViewTransition.showView(1, animation);
|
||||
} else {
|
||||
// Show mFailedView
|
||||
mViewTransition.showView(2, animation);
|
||||
switch (mState) {
|
||||
case STATE_NORMAL:
|
||||
// Show mMainView
|
||||
mViewTransition.showView(0, animation);
|
||||
// Show mBelowHeader
|
||||
mViewTransition2.showView(0, animation);
|
||||
break;
|
||||
case STATE_REFRESH:
|
||||
// Show mProgressView
|
||||
mViewTransition.showView(1, animation);
|
||||
break;
|
||||
case STATE_REFRESH_HEADER:
|
||||
// Show mMainView
|
||||
mViewTransition.showView(0, animation);
|
||||
// Show mProgress
|
||||
mViewTransition2.showView(1, animation);
|
||||
break;
|
||||
case STATE_FAILED:
|
||||
// Show mFailedView
|
||||
mViewTransition.showView(2, animation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,9 +626,6 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
}
|
||||
|
||||
private void bindComments(GalleryComment[] comments) {
|
||||
RippleSalon.addRipple(mComments, false);
|
||||
mComments.setOnClickListener(this);
|
||||
|
||||
mComments.removeViews(0, mComments.getChildCount() - 1);
|
||||
|
||||
final int maxShowCount = 2;
|
||||
@@ -629,11 +665,13 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
mPreviewText.setText(R.string.no_more_previews);
|
||||
} else {
|
||||
mPreviewText.setText(R.string.more_previews);
|
||||
RippleSalon.addRipple(mPreviews, false);
|
||||
mPreviews.setOnClickListener(this);
|
||||
}
|
||||
|
||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||
for (int i = 0, size = previewSet.size(); i < size; i++) {
|
||||
View view = inflater.inflate(R.layout.item_preview, mGridLayout, false);
|
||||
View view = inflater.inflate(R.layout.item_gallery_preview, mGridLayout, false);
|
||||
mGridLayout.addView(view);
|
||||
|
||||
int index = previewSet.getIndexAt(i);
|
||||
@@ -708,7 +746,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
|
||||
PopupMenu popup = new PopupMenu(getContext(), mOtherActions, Gravity.TOP);
|
||||
mPopupMenu = popup;
|
||||
popup.getMenuInflater().inflate(R.menu.gallery_detail_other_action, popup.getMenu());
|
||||
popup.getMenuInflater().inflate(R.menu.gallery_detail, popup.getMenu());
|
||||
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
@@ -729,7 +767,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
public void onClick(View v) {
|
||||
if (mFailedView == v) {
|
||||
if (request()) {
|
||||
mFailedText.setText("");
|
||||
mState = STATE_REFRESH;
|
||||
adjustViewVisibility(true);
|
||||
}
|
||||
} else if (mOtherActions == v) {
|
||||
@@ -760,6 +798,16 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelableArray(GalleryCommentsScene.KEY_COMMENTS, mGalleryDetail.comments);
|
||||
startScene(GalleryCommentsScene.class, args);
|
||||
} else if (mPreviews == v) {
|
||||
int gid = getGid();
|
||||
String token = getToken();
|
||||
if (gid == -1 || token == null) {
|
||||
return;
|
||||
}
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(GalleryPreviewsScene.KEY_GID, gid);
|
||||
args.putString(GalleryPreviewsScene.KEY_TOKEN, token);
|
||||
startScene(GalleryPreviewsScene.class, args);
|
||||
} else {
|
||||
Object tag;
|
||||
tag = v.getTag(R.id.tag);
|
||||
@@ -827,12 +875,15 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
}
|
||||
|
||||
private void onGetGalleryDetailSuccess(GalleryDetail result) {
|
||||
mState = STATE_NORMAL;
|
||||
mGalleryDetail = result;
|
||||
adjustViewVisibility(true);
|
||||
bindViewSecond();
|
||||
}
|
||||
|
||||
private void onGetGalleryDetailFailure(Exception e) {
|
||||
e.printStackTrace();
|
||||
mState = STATE_FAILED;
|
||||
String error = ExceptionUtils.getReadableString(getContext(), e);
|
||||
mFailedText.setText(error);
|
||||
adjustViewVisibility(true);
|
||||
@@ -869,6 +920,9 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
|
||||
|
||||
// Put gallery detail to cache
|
||||
EhApplication.getGalleryDetailCache(mApplication).put(result.gid, result);
|
||||
EhApplication.getLargePreviewSetCache(mApplication).put(
|
||||
EhCacheKeyFactory.getLargePreviewSetKey(result.gid, 0), result.previewSet);
|
||||
EhApplication.getPreviewPagesCache(mApplication).put(result.gid, result.previewPages);
|
||||
|
||||
// Notify success
|
||||
GalleryDetailScene scene = getScene();
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.ehviewer.ui.scene;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.hippo.easyrecyclerview.EasyRecyclerView;
|
||||
import com.hippo.easyrecyclerview.MarginItemDecoration;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.EhCacheKeyFactory;
|
||||
import com.hippo.ehviewer.client.EhClient;
|
||||
import com.hippo.ehviewer.client.EhRequest;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.ehviewer.client.data.GalleryPreview;
|
||||
import com.hippo.ehviewer.client.data.LargePreviewSet;
|
||||
import com.hippo.ehviewer.client.exception.EhException;
|
||||
import com.hippo.scene.SceneApplication;
|
||||
import com.hippo.scene.SceneFragment;
|
||||
import com.hippo.scene.StageActivity;
|
||||
import com.hippo.vector.VectorDrawable;
|
||||
import com.hippo.widget.ContentLayout;
|
||||
import com.hippo.widget.LoadImageView;
|
||||
import com.hippo.widget.Slider;
|
||||
import com.hippo.yorozuya.LayoutUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
public class GalleryPreviewsScene extends ToolbarScene {
|
||||
|
||||
public static final String KEY_GID = "gid";
|
||||
public static final String KEY_TOKEN = "token";
|
||||
public final static String KEY_HAS_FIRST_REFRESH = "has_first_refresh";
|
||||
|
||||
private EhClient mClient;
|
||||
private GalleryPreviewAdapter mAdapter;
|
||||
private GalleryPreviewHelper mHelper;
|
||||
private int mGid = -1;
|
||||
private String mToken = null;
|
||||
private boolean mHasFirstRefresh = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mClient = EhApplication.getEhClient(getContext());
|
||||
if (savedInstanceState == null) {
|
||||
onInit();
|
||||
} else {
|
||||
onRestore(savedInstanceState);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlerArgs(Bundle args) {
|
||||
if (args == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mGid = args.getInt(KEY_GID, -1);
|
||||
mToken = args.getString(KEY_TOKEN, null);
|
||||
}
|
||||
|
||||
private void onInit() {
|
||||
handlerArgs(getArguments());
|
||||
}
|
||||
|
||||
private void onRestore(@NonNull Bundle savedInstanceState) {
|
||||
mGid = savedInstanceState.getInt(KEY_GID, -1);
|
||||
mToken = savedInstanceState.getString(KEY_TOKEN, null);
|
||||
mHasFirstRefresh = savedInstanceState.getBoolean(KEY_HAS_FIRST_REFRESH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt(KEY_GID, mGid);
|
||||
outState.putString(KEY_TOKEN, mToken);
|
||||
outState.putBoolean(KEY_HAS_FIRST_REFRESH, mHasFirstRefresh);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView2(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
ContentLayout contentLayout = (ContentLayout) inflater.inflate(
|
||||
R.layout.scene_gallery_previews, container, false);
|
||||
contentLayout.hideFastScroll();
|
||||
EasyRecyclerView recyclerView = contentLayout.getRecyclerView();
|
||||
|
||||
mAdapter = new GalleryPreviewAdapter();
|
||||
recyclerView.setAdapter(mAdapter);
|
||||
recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 3)); // TODO hardcode
|
||||
int padding = LayoutUtils.dp2pix(getContext(), 4);
|
||||
recyclerView.setPadding(padding, padding, padding, padding);
|
||||
recyclerView.setClipToPadding(false);
|
||||
recyclerView.addItemDecoration(new MarginItemDecoration(padding));
|
||||
|
||||
mHelper = new GalleryPreviewHelper();
|
||||
contentLayout.setHelper(mHelper);
|
||||
|
||||
// Only refresh for the first time
|
||||
if (!mHasFirstRefresh) {
|
||||
mHasFirstRefresh = true;
|
||||
mHelper.firstRefresh();
|
||||
}
|
||||
|
||||
return contentLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setTitle(R.string.gallery_previews);
|
||||
setNavigationIcon(VectorDrawable.create(getContext(), R.drawable.ic_arrow_left));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMenuResId() {
|
||||
return R.menu.gallery_previews;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
switch (id) {
|
||||
case R.id.action_go_to:
|
||||
int pages = mHelper.getPages();
|
||||
if (pages > 0 && mHelper.canGoTo()) {
|
||||
GoToDialogHelper helper = new GoToDialogHelper(pages, mHelper.getPageForTop());
|
||||
AlertDialog dialog = new AlertDialog.Builder(getContext()).setTitle(R.string.go_to)
|
||||
.setView(helper.getView())
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.create();
|
||||
dialog.show();
|
||||
helper.setPositiveButtonClickListener(dialog);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNavigationClick() {
|
||||
finish();
|
||||
}
|
||||
|
||||
private class GalleryPreviewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public LoadImageView image;
|
||||
public TextView text;
|
||||
|
||||
public GalleryPreviewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
image = (LoadImageView) itemView.findViewById(R.id.image);
|
||||
text = (TextView) itemView.findViewById(R.id.text);
|
||||
}
|
||||
}
|
||||
|
||||
private class GalleryPreviewAdapter extends RecyclerView.Adapter<GalleryPreviewHolder> {
|
||||
|
||||
@Override
|
||||
public GalleryPreviewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new GalleryPreviewHolder(getActivity().getLayoutInflater().inflate(R.layout.item_gallery_preview, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(GalleryPreviewHolder holder, int position) {
|
||||
GalleryPreview preview = mHelper.getDataAt(position);
|
||||
holder.image.load(EhCacheKeyFactory.getLargePreviewKey(mGid, preview.index), preview.imageUrl);
|
||||
holder.text.setText(String.format(Locale.US, "%d", preview.index + 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mHelper.size();
|
||||
}
|
||||
}
|
||||
|
||||
private class GalleryPreviewHelper extends ContentLayout.ContentHelper<GalleryPreview> {
|
||||
|
||||
@Override
|
||||
protected void getPageData(int taskId, int type, int page) {
|
||||
if (mGid == -1 || mToken == null) {
|
||||
onGetExpection(taskId, new EhException(getString(R.string.error_cannot_find_gallery)));
|
||||
return;
|
||||
}
|
||||
|
||||
LargePreviewSet previewSet = EhApplication.getLargePreviewSetCache(
|
||||
getContext()).get(EhCacheKeyFactory.getLargePreviewSetKey(mGid, page));
|
||||
Integer pages = EhApplication.getPreviewPagesCache(getContext()).get(mGid);
|
||||
if (previewSet != null && pages != null) {
|
||||
onGetLargePreviewSetSuccess(Pair.create(previewSet, pages), taskId);
|
||||
return;
|
||||
}
|
||||
|
||||
String url = EhUrl.getGalleryDetailUrl(mGid, mToken, page);
|
||||
EhRequest request = new EhRequest();
|
||||
request.setMethod(EhClient.METHOD_GET_LARGE_PREVIEW_SET);
|
||||
request.setCallback(new GetLargePreviewSetListener(getContext(),
|
||||
((StageActivity) getActivity()).getStageId(), getTag(), taskId, mGid, page));
|
||||
request.setArgs(url);
|
||||
mClient.execute(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Context getContext() {
|
||||
return GalleryPreviewsScene.this.getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyDataSetChanged() {
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyItemRangeRemoved(int positionStart, int itemCount) {
|
||||
mAdapter.notifyItemRangeRemoved(positionStart, itemCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyItemRangeInserted(int positionStart, int itemCount) {
|
||||
mAdapter.notifyItemRangeInserted(positionStart, itemCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void onGetLargePreviewSetSuccess(Pair<LargePreviewSet, Integer> result, int taskId) {
|
||||
if (mHelper != null && mHelper.isCurrentTask(taskId)) {
|
||||
LargePreviewSet previewSet = result.first;
|
||||
int size = previewSet.size();
|
||||
ArrayList<GalleryPreview> list = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
GalleryPreview preview = new GalleryPreview();
|
||||
preview.imageUrl = previewSet.getImageUrlAt(i);
|
||||
preview.pageUrl = previewSet.getPageUrlAt(i);
|
||||
preview.index = previewSet.getIndexAt(i);
|
||||
list.add(preview);
|
||||
}
|
||||
|
||||
mHelper.setPages(taskId, result.second);
|
||||
mHelper.onGetPageData(taskId, list);
|
||||
}
|
||||
}
|
||||
|
||||
private void onGetLargePreviewSetFailure(Exception e, int taskId) {
|
||||
if (mHelper != null && mHelper.isCurrentTask(taskId)) {
|
||||
mHelper.onGetExpection(taskId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class GetLargePreviewSetListener implements EhClient.Callback<Pair<LargePreviewSet, Integer>> {
|
||||
|
||||
private SceneApplication mApplication;
|
||||
private int mStageId;
|
||||
private String mSceneTag;
|
||||
private int mTaskId;
|
||||
private int mGid;
|
||||
private int mPage;
|
||||
|
||||
public GetLargePreviewSetListener(Context context, int stageId, String sceneTag, int taskId, int gid, int page) {
|
||||
mApplication = (SceneApplication) context.getApplicationContext();
|
||||
mStageId = stageId;
|
||||
mSceneTag = sceneTag;
|
||||
mTaskId = taskId;
|
||||
mGid = gid;
|
||||
mPage = page;
|
||||
}
|
||||
|
||||
private GalleryPreviewsScene getScene() {
|
||||
StageActivity stage = mApplication.findStageActivityById(mStageId);
|
||||
if (stage == null) {
|
||||
return null;
|
||||
}
|
||||
SceneFragment scene = stage.findSceneByTag(mSceneTag);
|
||||
if (scene instanceof GalleryPreviewsScene) {
|
||||
return (GalleryPreviewsScene) scene;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Pair<LargePreviewSet, Integer> result) {
|
||||
EhApplication.getLargePreviewSetCache(mApplication).put(
|
||||
EhCacheKeyFactory.getLargePreviewSetKey(mGid, mPage), result.first);
|
||||
EhApplication.getPreviewPagesCache(mApplication).put(mGid, result.second);
|
||||
|
||||
GalleryPreviewsScene scene = getScene();
|
||||
if (scene != null) {
|
||||
scene.onGetLargePreviewSetSuccess(result, mTaskId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
GalleryPreviewsScene scene = getScene();
|
||||
if (scene != null) {
|
||||
scene.onGetLargePreviewSetFailure(e, mTaskId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class GoToDialogHelper implements View.OnClickListener,
|
||||
DialogInterface.OnDismissListener {
|
||||
|
||||
private int mPages;
|
||||
|
||||
private View mView;
|
||||
private Slider mSlider;
|
||||
|
||||
private Dialog mDialog;
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
private GoToDialogHelper(int pages, int currentPage) {
|
||||
mPages = pages;
|
||||
mView = getActivity().getLayoutInflater().inflate(R.layout.dialog_go_to, null);
|
||||
((TextView) mView.findViewById(R.id.start)).setText("1");
|
||||
((TextView) mView.findViewById(R.id.end)).setText(String.format(Locale.US, "%d", pages));
|
||||
mSlider = (Slider) mView.findViewById(R.id.slider);
|
||||
mSlider.setRange(1, pages);
|
||||
mSlider.setProgress(currentPage + 1);
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
return mView;
|
||||
}
|
||||
|
||||
public void setPositiveButtonClickListener(AlertDialog dialog) {
|
||||
mDialog = dialog;
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(this);
|
||||
dialog.setOnDismissListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int page = mSlider.getProgress() - 1;
|
||||
if (page >= 0 && page < mPages) {
|
||||
mHelper.goTo(page);
|
||||
if (mDialog != null) {
|
||||
mDialog.dismiss();
|
||||
mDialog = null;
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(getContext(), R.string.error_out_of_range, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
mDialog = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* Copyright 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
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* 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.animation.ValueAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.AbsoluteLayout;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import com.hippo.anani.AnimationUtils;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.vector.VectorDrawable;
|
||||
import com.hippo.yorozuya.LayoutUtils;
|
||||
import com.hippo.yorozuya.MathUtils;
|
||||
import com.hippo.yorozuya.SimpleHandler;
|
||||
|
||||
public class Slider extends View {
|
||||
|
||||
private static char[] CHARACTERS = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
|
||||
};
|
||||
|
||||
private static final int BUBBLE_WIDTH = 26;
|
||||
private static final int BUBBLE_HEIGHT = 32;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private Paint mPaint;
|
||||
private Paint mBgPaint;
|
||||
|
||||
private RectF mLeftRectF = new RectF();
|
||||
private RectF mRightRectF = new RectF();
|
||||
|
||||
private PopupWindow mPopup;
|
||||
private BubbleView mBubble;
|
||||
|
||||
private int mStart;
|
||||
private int mEnd;
|
||||
private int mProgress;
|
||||
private float mPercent;
|
||||
private int mDrawProgress;
|
||||
private float mDrawPercent;
|
||||
private int mTargetProgress;
|
||||
|
||||
private float mThickness;
|
||||
private float mRadius;
|
||||
|
||||
private float mCharWidth;
|
||||
private float mCharHeight;
|
||||
|
||||
private int mBubbleWidth;
|
||||
private int mBubbleHeight;
|
||||
private int mBubbleMinWidth;
|
||||
private int mBubbleMinHeight;
|
||||
|
||||
private int mPopupX;
|
||||
private int mPopupY;
|
||||
private int mPopupWidth;
|
||||
|
||||
private int[] mTemp = new int[2];
|
||||
|
||||
private boolean mReverse = false;
|
||||
|
||||
private boolean mShowBubble;
|
||||
|
||||
private float mDrawBubbleScale = 0f;
|
||||
|
||||
private ValueAnimator mProgressAnimation;
|
||||
private ValueAnimator mBubbleScaleAnimation;
|
||||
|
||||
private OnSetProgressListener mListener;
|
||||
|
||||
private CheckForShowBubble mCheckForShowBubble;
|
||||
|
||||
public Slider(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
public Slider(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
mContext = context;
|
||||
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
textPaint.setTextAlign(Paint.Align.CENTER);
|
||||
|
||||
mBubbleMinWidth = LayoutUtils.dp2pix(context, BUBBLE_WIDTH);
|
||||
mBubbleMinHeight = LayoutUtils.dp2pix(context, BUBBLE_HEIGHT);
|
||||
|
||||
mBubble = new BubbleView(context, textPaint);
|
||||
mBubble.setScaleX(0.0f);
|
||||
mBubble.setScaleY(0.0f);
|
||||
AbsoluteLayout absoluteLayout = new AbsoluteLayout(context);
|
||||
absoluteLayout.addView(mBubble);
|
||||
absoluteLayout.setBackgroundDrawable(null);
|
||||
mPopup = new PopupWindow(absoluteLayout);
|
||||
mPopup.setOutsideTouchable(false);
|
||||
mPopup.setTouchable(false);
|
||||
mPopup.setFocusable(false);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Slider);
|
||||
textPaint.setColor(a.getColor(R.styleable.Slider_textColor, Color.WHITE));
|
||||
textPaint.setTextSize(a.getDimensionPixelSize(R.styleable.Slider_textSize, 12));
|
||||
|
||||
updateTextSize();
|
||||
|
||||
setRange(a.getInteger(R.styleable.Slider_start, 0), a.getInteger(R.styleable.Slider_end, 0));
|
||||
setProgress(a.getInteger(R.styleable.Slider_slider_progress, 0));
|
||||
mThickness = a.getDimension(R.styleable.Slider_thickness, 2);
|
||||
mRadius = a.getDimension(R.styleable.Slider_radius, 6);
|
||||
setColor(a.getColor(R.styleable.Slider_color, Color.BLACK));
|
||||
|
||||
mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mBgPaint.setColor(a.getBoolean(R.styleable.Slider_dark, false) ? 0x4dffffff : 0x42000000);
|
||||
|
||||
a.recycle();
|
||||
|
||||
mProgressAnimation = new ValueAnimator();
|
||||
mProgressAnimation.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
|
||||
mProgressAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
|
||||
float value = (Float) animation.getAnimatedValue();
|
||||
mDrawPercent = value;
|
||||
mDrawProgress = Math.round(MathUtils.lerp((float) mStart, mEnd, value));
|
||||
updateBubblePosition();
|
||||
mBubble.setProgress(mDrawProgress);
|
||||
invalidate();
|
||||
}
|
||||
});
|
||||
|
||||
mBubbleScaleAnimation = new ValueAnimator();
|
||||
mBubbleScaleAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
|
||||
float value = (Float) animation.getAnimatedValue();
|
||||
mDrawBubbleScale = value;
|
||||
mBubble.setScaleX(value);
|
||||
mBubble.setScaleY(value);
|
||||
invalidate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateTextSize() {
|
||||
int length = CHARACTERS.length;
|
||||
float[] widths = new float[length];
|
||||
mPaint.getTextWidths(CHARACTERS, 0, length, widths);
|
||||
mCharWidth = 0.0f;
|
||||
for (float f : widths) {
|
||||
mCharWidth = Math.max(mCharWidth, f);
|
||||
}
|
||||
|
||||
Paint.FontMetrics fm = mPaint.getFontMetrics();
|
||||
mCharHeight = fm.bottom - fm.top;
|
||||
}
|
||||
|
||||
private void updateBubbleSize() {
|
||||
int oldWidth = mBubbleWidth;
|
||||
int oldHeight = mBubbleHeight;
|
||||
mBubbleWidth = (int) Math.max(mBubbleMinWidth,
|
||||
Integer.toString(mEnd).length() * mCharWidth + LayoutUtils.dp2pix(mContext, 8));
|
||||
mBubbleHeight = (int) Math.max(mBubbleMinHeight,
|
||||
mCharHeight + LayoutUtils.dp2pix(mContext, 8));
|
||||
|
||||
if (oldWidth != mBubbleWidth && oldHeight != mBubbleHeight) {
|
||||
//noinspection deprecation
|
||||
AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams) mBubble.getLayoutParams();
|
||||
lp.width = mBubbleWidth;
|
||||
lp.height = mBubbleHeight;
|
||||
mBubble.setLayoutParams(lp);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePopup() {
|
||||
int width = getWidth();
|
||||
int paddingTop = getPaddingTop();
|
||||
int paddingBottom = getPaddingBottom();
|
||||
|
||||
getLocationInWindow(mTemp);
|
||||
|
||||
mPopupWidth = (int) (width - mRadius - mRadius + mBubbleWidth);
|
||||
int popupHeight = mBubbleHeight;
|
||||
mPopupX = (int) (mTemp[0] + mRadius - (mBubbleWidth / 2));
|
||||
mPopupY = (int) (mTemp[1] - popupHeight + paddingTop +
|
||||
((getHeight() - paddingTop - paddingBottom) / 2) -
|
||||
mRadius -LayoutUtils.dp2pix(mContext, 2));
|
||||
|
||||
mPopup.update(mPopupX, mPopupY, mPopupWidth, popupHeight, false);
|
||||
}
|
||||
|
||||
private void updateBubblePosition() {
|
||||
float x = ((mPopupWidth - mBubbleWidth) * mDrawPercent);
|
||||
mBubble.setX(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
|
||||
updatePopup();
|
||||
updateBubblePosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
mPopup.showAtLocation(this, Gravity.TOP | Gravity.LEFT, mPopupX, mPopupY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
|
||||
mPopup.dismiss();
|
||||
}
|
||||
|
||||
private void startProgressAnimation(float percent) {
|
||||
float currentValue;
|
||||
if (mProgressAnimation.isRunning()) {
|
||||
mProgressAnimation.setCurrentPlayTime(mProgressAnimation.getCurrentPlayTime());
|
||||
Object value = mProgressAnimation.getAnimatedValue();
|
||||
if (value instanceof Float) {
|
||||
currentValue = (float) value;
|
||||
} else {
|
||||
currentValue = mDrawPercent;
|
||||
}
|
||||
} else {
|
||||
currentValue = mDrawPercent;
|
||||
}
|
||||
mProgressAnimation.cancel();
|
||||
mProgressAnimation.setFloatValues(currentValue, percent);
|
||||
mProgressAnimation.setDuration(Math.min(500, (long) (50 * getWidth() * Math.abs(currentValue - percent))));
|
||||
mProgressAnimation.start();
|
||||
}
|
||||
|
||||
private void startShowBubbleAnimation() {
|
||||
mBubbleScaleAnimation.cancel();
|
||||
mBubbleScaleAnimation.setFloatValues(mDrawBubbleScale, 1.0f);
|
||||
mBubbleScaleAnimation.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
|
||||
mBubbleScaleAnimation.setDuration((long) (300 * Math.abs(mDrawBubbleScale - 1.0f)));
|
||||
mBubbleScaleAnimation.start();
|
||||
}
|
||||
|
||||
private void startHideBubbleAnimation() {
|
||||
mBubbleScaleAnimation.cancel();
|
||||
mBubbleScaleAnimation.setFloatValues(mDrawBubbleScale, 0.0f);
|
||||
mBubbleScaleAnimation.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR);
|
||||
mBubbleScaleAnimation.setDuration((long) (300 * Math.abs(mDrawBubbleScale - 0.0f)));
|
||||
mBubbleScaleAnimation.start();
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
mPaint.setColor(color);
|
||||
mBubble.setColor(color);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRange(int start, int end) {
|
||||
mStart = start;
|
||||
mEnd = end;
|
||||
setProgress(mProgress);
|
||||
|
||||
updateBubbleSize();
|
||||
}
|
||||
|
||||
public void setProgress(int progress) {
|
||||
progress = MathUtils.clamp(progress, mStart, mEnd);
|
||||
if (mProgress != progress) {
|
||||
int oldProgress = mProgress;
|
||||
mProgress = progress;
|
||||
mPercent = MathUtils.delerp(mStart, mEnd, mProgress);
|
||||
mTargetProgress = progress;
|
||||
|
||||
if (mProgressAnimation == null) {
|
||||
// For init
|
||||
mDrawPercent = mPercent;
|
||||
mDrawProgress = mProgress;
|
||||
updateBubblePosition();
|
||||
mBubble.setProgress(mDrawProgress);
|
||||
} else {
|
||||
startProgressAnimation(mPercent);
|
||||
}
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onSetProgress(this, progress, oldProgress, false, true);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public int getProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
|
||||
public void setReverse(boolean reverse) {
|
||||
if (mReverse != reverse) {
|
||||
mReverse = reverse;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnSetProgressListener(OnSetProgressListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(@NonNull Canvas canvas) {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
if (width < LayoutUtils.dp2pix(mContext, 24)) {
|
||||
canvas.drawRect(0, 0, width, getHeight(), mPaint);
|
||||
} else {
|
||||
int paddingLeft = getPaddingLeft();
|
||||
int paddingTop = getPaddingTop();
|
||||
int paddingRight = getPaddingRight();
|
||||
int paddingBottom = getPaddingBottom();
|
||||
float thickness = mThickness;
|
||||
float radius = mRadius;
|
||||
float halfThickness = thickness / 2;
|
||||
|
||||
int saved = canvas.save();
|
||||
|
||||
canvas.translate(0, paddingTop + ((height - paddingTop - paddingBottom) / 2));
|
||||
|
||||
float currentX = paddingLeft + radius + (width - radius - radius - paddingLeft - paddingRight) *
|
||||
(mReverse ? (1.0f - mDrawPercent) : mDrawPercent);
|
||||
|
||||
mLeftRectF.set(paddingLeft + radius, -halfThickness, currentX, halfThickness);
|
||||
mRightRectF.set(currentX, -halfThickness, width - paddingRight - radius, halfThickness);
|
||||
|
||||
// Draw bar
|
||||
if (mReverse) {
|
||||
canvas.drawRect(mRightRectF, mPaint);
|
||||
canvas.drawRect(mLeftRectF, mBgPaint);
|
||||
} else {
|
||||
canvas.drawRect(mLeftRectF, mPaint);
|
||||
canvas.drawRect(mRightRectF, mBgPaint);
|
||||
}
|
||||
|
||||
// Draw controller
|
||||
float scale = 1.0f - mDrawBubbleScale;
|
||||
if (scale != 0.0f) {
|
||||
canvas.scale(scale, scale, currentX, 0);
|
||||
canvas.drawCircle(currentX, 0, radius, mPaint);
|
||||
}
|
||||
|
||||
canvas.restoreToCount(saved);
|
||||
}
|
||||
}
|
||||
|
||||
private void setShowBubble(boolean showBubble) {
|
||||
if (mShowBubble != showBubble) {
|
||||
mShowBubble = showBubble;
|
||||
if (showBubble) {
|
||||
startShowBubbleAnimation();
|
||||
} else {
|
||||
startHideBubbleAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(@NonNull MotionEvent event) {
|
||||
|
||||
int action = event.getAction();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
int paddingLeft = getPaddingLeft();
|
||||
int paddingRight = getPaddingRight();
|
||||
float radius = mRadius;
|
||||
float x = event.getX();
|
||||
int progress = Math.round(MathUtils.lerp((float) mStart, (float) mEnd,
|
||||
MathUtils.clamp((mReverse ? (getWidth() - paddingLeft - radius - x) : (x - radius - paddingLeft)) /
|
||||
(getWidth() - radius - radius - paddingLeft - paddingRight), 0.0f, 1.0f)));
|
||||
float percent = MathUtils.delerp(mStart, mEnd, progress);
|
||||
|
||||
// ACTION_CANCEL not changed
|
||||
if (action == MotionEvent.ACTION_CANCEL) {
|
||||
progress = mProgress;
|
||||
percent = mPercent;
|
||||
}
|
||||
|
||||
if (mTargetProgress != progress) {
|
||||
mTargetProgress = progress;
|
||||
startProgressAnimation(percent);
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onSetProgress(this, progress, progress, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
||||
SimpleHandler.getInstance().removeCallbacks(mCheckForShowBubble);
|
||||
setShowBubble(false);
|
||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
||||
if (mCheckForShowBubble == null) {
|
||||
mCheckForShowBubble = new CheckForShowBubble();
|
||||
}
|
||||
SimpleHandler.getInstance().postDelayed(mCheckForShowBubble, ViewConfiguration.getTapTimeout());
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
if (mProgress != progress) {
|
||||
int oldProgress = mProgress;
|
||||
mProgress = progress;
|
||||
mPercent = mDrawPercent;
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onSetProgress(this, progress, oldProgress, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
private static class BubbleView extends View {
|
||||
|
||||
private static final float TEXT_CENTER = (float) BUBBLE_WIDTH / 2.0f / BUBBLE_HEIGHT;
|
||||
|
||||
private VectorDrawable mVectorDrawable;
|
||||
|
||||
private Paint mTextPaint;
|
||||
|
||||
private String mProgressStr = "";
|
||||
|
||||
private Rect mRect = new Rect();
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public BubbleView(Context context, Paint paint) {
|
||||
super(context);
|
||||
mVectorDrawable = VectorDrawable.create(context, R.drawable.slider_bubble);
|
||||
setBackgroundDrawable(mVectorDrawable);
|
||||
mTextPaint = paint;
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
Object obj = mVectorDrawable.getTargetByName("bubble");
|
||||
if (obj instanceof VectorDrawable.VFullPath) {
|
||||
((VectorDrawable.VFullPath) obj).setFillColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
public void setProgress(int progress) {
|
||||
String str = Integer.toString(progress);
|
||||
if (!str.equals(mProgressStr)) {
|
||||
mProgressStr = str;
|
||||
mTextPaint.getTextBounds(str, 0, str.length(), mRect);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
setPivotX(w / 2);
|
||||
setPivotY(h);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(@NonNull Canvas canvas) {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
int x = width / 2;
|
||||
int y = (int) ((height * TEXT_CENTER) + (mRect.height() / 2));
|
||||
canvas.drawText(mProgressStr, x, y, mTextPaint);
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnSetProgressListener {
|
||||
|
||||
void onSetProgress(Slider slider, int newProgress, int oldProgress, boolean byUser, boolean confirm);
|
||||
}
|
||||
|
||||
private final class CheckForShowBubble implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
setShowBubble(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user