Update detail scene

This commit is contained in:
seven332 committed 2015-06-29 15:13:37 +08:00
1 parent a26686c31b
commit 57acf0585d
15 files changed
+973 -71

No files matched your search

@@ -0,0 +1,53 @@
package com.hippo.drawable;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
import com.hippo.conaco.Conaco;
import com.hippo.conaco.Unikery;
import com.hippo.ehviewer.R;
public class UnikeryDrawable extends WrapDrawable implements Unikery {
private int mTaskId;
private View mView;
public UnikeryDrawable(View view) {
mView = view;
}
@Override
public void setBitmap(Bitmap bitmap, Conaco.Source source) {
setDrawable(new BitmapDrawable(mView.getResources(), bitmap));
}
@Override
public void setDrawable(Drawable drawable) {
super.setDrawable(drawable);
updateBounds();
mView.requestLayout();
}
@Override
public void setTaskId(int id) {
mTaskId = id;
}
@Override
public int getTaskId() {
return mTaskId;
}
@Override
public void onFailure() {
setDrawable(mView.getResources().getDrawable(R.drawable.ic_sad));
}
@Override
public void onCancel() {
// Empty
}
}
@@ -0,0 +1,123 @@
package com.hippo.drawable;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
public class WrapDrawable extends Drawable {
private Drawable mDrawable;
public void setDrawable(Drawable drawable) {
mDrawable = drawable;
}
public void updateBounds() {
setBounds(0, 0, getIntrinsicWidth(), getIntrinsicHeight());
}
@Override
public void draw(Canvas canvas) {
if (mDrawable != null) {
mDrawable.draw(canvas);
}
}
@Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
if (mDrawable != null) {
mDrawable.setBounds(left, top, right, bottom);
}
}
@Override
public void setBounds(Rect bounds) {
super.setBounds(bounds);
if (mDrawable != null) {
mDrawable.setBounds(bounds);
}
}
@Override
public void setChangingConfigurations(int configs) {
super.setChangingConfigurations(configs);
if (mDrawable != null) {
mDrawable.setChangingConfigurations(configs);
}
}
@Override
public int getChangingConfigurations() {
if (mDrawable != null) {
return mDrawable.getChangingConfigurations();
} else {
return super.getChangingConfigurations();
}
}
@Override
public void setDither(boolean dither) {
super.setDither(dither);
if (mDrawable != null) {
mDrawable.setDither(dither);
}
}
@Override
public void setFilterBitmap(boolean filter) {
super.setFilterBitmap(filter);
if (mDrawable != null) {
mDrawable.setFilterBitmap(filter);
}
}
@Override
public void setAlpha(int alpha) {
if (mDrawable != null) {
mDrawable.setAlpha(alpha);
}
}
@Override
public void setColorFilter(ColorFilter cf) {
if (mDrawable != null) {
mDrawable.setColorFilter(cf);
}
}
@Override
public int getOpacity() {
if (mDrawable != null) {
return mDrawable.getOpacity();
} else {
return PixelFormat.UNKNOWN;
}
}
@Override
public int getIntrinsicWidth() {
if (mDrawable != null) {
return mDrawable.getIntrinsicWidth();
} else {
return super.getIntrinsicWidth();
}
}
@Override
public int getIntrinsicHeight() {
if (mDrawable != null) {
return mDrawable.getIntrinsicHeight();
} else {
return super.getIntrinsicHeight();
}
}
}
@@ -22,15 +22,28 @@ import com.hippo.ehviewer.util.EhUtils;
import com.hippo.util.AssertUtils;
import com.hippo.util.Utils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GalleryDetailParser {
private static final DateFormat WEB_COMMENT_DATA_FORMAT = new SimpleDateFormat("dd MMMMM yyyy, HH:mm z", Locale.US);
private static final DateFormat OUT_COMMENT_DATA_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
static {
WEB_COMMENT_DATA_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
OUT_COMMENT_DATA_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
}
private static final String OFFENSIVE_STRING =
"<p>(And if you choose to ignore this warning, you lose all rights to complain about it in the future.)</p>";
private static final String PINING_STRING =
@@ -209,6 +222,7 @@ public class GalleryDetailParser {
TagGroup tagGroup = new TagGroup();
tagGroup.groupName = sortOut(m.group(1));
parseTagGroup(tagGroup, m.group(0));
tags.add(tagGroup);
}
}
@@ -232,7 +246,15 @@ public class GalleryDetailParser {
m = p.matcher(body);
comments = new LinkedList<>();
while (m.find()) {
comments.add(new Comment(sortOut(m.group(1)), sortOut(m.group(2)), m.group(3)));
String webDateString = sortOut(m.group(1));
Date date;
try {
date = WEB_COMMENT_DATA_FORMAT.parse(webDateString);
} catch (java.text.ParseException e) {
date = new Date(0l);
}
String outDateString = OUT_COMMENT_DATA_FORMAT.format(date);
comments.add(new Comment(outDateString, date, sortOut(m.group(2)), m.group(3)));
}
Collections.sort(comments, COMMENT_SORTER);
}
@@ -257,67 +279,10 @@ public class GalleryDetailParser {
}
static class CommentSort implements Comparator<Comment> {
private int compareNum(String n1, String n2, int median) {
int re = 0;
for (int i = 0; i < median; i++) {
re *= 10;
re += n1.charAt(i) - n2.charAt(i);
}
return re;
}
private int getMonth(String m) {
if (m.equals("January"))
return 1;
else if (m.equals("February"))
return 2;
else if (m.equals("March"))
return 3;
else if (m.equals("April"))
return 4;
else if (m.equals("May"))
return 5;
else if (m.equals("June"))
return 6;
else if (m.equals("July"))
return 7;
else if (m.equals("August"))
return 8;
else if (m.equals("September"))
return 9;
else if (m.equals("October"))
return 10;
else if (m.equals("November"))
return 11;
else if (m.equals("December"))
return 12;
else
return 0;
}
@Override
public int compare(Comment c1, Comment c2) {
int re = 0;
Pattern p = Pattern.compile("(\\d{2}) (\\w+) (\\d{4}), (\\d{2}):(\\d{2})");
Matcher m1 = p.matcher(c1.time);
Matcher m2 = p.matcher(c2.time);
if (!m1.find() || !m2.find())
return 0;
// year
re += compareNum(m1.group(3), m2.group(3), 4);
re <<= 4;
// Month
re += getMonth(m1.group(2)) - getMonth(m2.group(2));
re <<= 5;
// Day
re += compareNum(m1.group(1), m2.group(1), 2);
re <<= 5;
// Hour
re += compareNum(m1.group(4), m2.group(4), 2);
re <<= 6;
// Minute
re += compareNum(m1.group(5), m2.group(5), 2);
return re;
return c1.date.compareTo(c2.date);
}
}
}
@@ -16,13 +16,17 @@
package com.hippo.ehviewer.data;
import java.util.Date;
public class Comment {
public String time;
public Date date;
public String user;
public String comment;
public Comment (String time, String user, String comment) {
public Comment (String time, Date date, String user, String comment) {
this.time = time;
this.date = date;
this.user = user;
this.comment = comment;
}
@@ -16,14 +16,19 @@
package com.hippo.ehviewer.ui.scene;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.text.Html;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextSwitcher;
import android.widget.TextView;
import android.widget.ViewSwitcher;
@@ -34,21 +39,31 @@ import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.EhImageKeyFactory;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.EhClient;
import com.hippo.ehviewer.data.Comment;
import com.hippo.ehviewer.data.GalleryDetail;
import com.hippo.ehviewer.data.GalleryInfo;
import com.hippo.ehviewer.data.TagGroup;
import com.hippo.ehviewer.util.Config;
import com.hippo.ehviewer.util.EhUtils;
import com.hippo.ehviewer.widget.LoadImageView;
import com.hippo.ehviewer.widget.RatingView;
import com.hippo.miscellaneous.StartActivityHelper;
import com.hippo.rippleold.RippleSalon;
import com.hippo.scene.Announcer;
import com.hippo.scene.Scene;
import com.hippo.util.Log;
import com.hippo.util.URLImageGetter;
import com.hippo.util.UiUtils;
import com.hippo.widget.AutoWrapLayout;
import java.util.List;
public class GalleryDetailScene extends Scene implements View.OnClickListener {
public static final String KEY_GALLERY_INFO = "gallery_info";
private FrameLayout mDetailFrameLayout;
private ViewGroup mHeader;
LoadImageView mThumb;
TextView mTitle;
@@ -72,11 +87,16 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
private TextView mTorrent;
private TextView mShare;
private TextView mRate;
private LinearLayout mTag;
private LinearLayout mComment;
private TextView mCommentMore;
private View mProgressBar;
private ViewTransition mViewTransition;
private GalleryDetail mGalleryDetail;
private void handleAnnouncer(Announcer announcer) {
GalleryInfo gi;
@@ -100,13 +120,7 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
Context context = getStageActivity();
Resources resources = context.getResources();
mViewTransition.showView(1);
RippleSalon.addRipple(mRead, false);
RippleSalon.addRipple(mDownload, false);
mRead.setOnClickListener(this);
mDownload.setOnClickListener(this);
mViewTransition.showView(1, false);
mRatingText.setFactory(new RatingTextViewFactory());
@@ -125,6 +139,20 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
mTorrent.setCompoundDrawables(null, torrentDrawable, null, null);
mShare.setCompoundDrawables(null, shareDrawable, null, null);
mRate.setCompoundDrawables(null, rateDrawable, null, null);
mRead.setOnClickListener(this);
mDownload.setOnClickListener(this);
mFavorite.setOnClickListener(this);
mTorrent.setOnClickListener(this);
mShare.setOnClickListener(this);
mRate.setOnClickListener(this);
RippleSalon.addRipple(mRead, false);
RippleSalon.addRipple(mDownload, false);
RippleSalon.addRipple(mFavorite, false);
RippleSalon.addRipple(mTorrent, false);
RippleSalon.addRipple(mShare, false);
RippleSalon.addRipple(mRate, false);
}
@Override
@@ -132,6 +160,8 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
super.onCreate(savedInstanceState);
setContentView(R.layout.scene_gallery_detail);
mDetailFrameLayout = (FrameLayout) findViewById(R.id.detail_frame_layout);
mHeader = (ViewGroup) findViewById(R.id.header);
mThumb = (LoadImageView) mHeader.findViewById(R.id.thumb);
mTitle = (TextView) mHeader.findViewById(R.id.title);
@@ -155,6 +185,9 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
mTorrent = (TextView) mContent.findViewById(R.id.torrent);
mShare = (TextView) mContent.findViewById(R.id.share);
mRate = (TextView) mContent.findViewById(R.id.rate);
mTag = (LinearLayout) mContent.findViewById(R.id.tag);
mComment = (LinearLayout) mContent.findViewById(R.id.comment);
mCommentMore = (TextView) mContent.findViewById(R.id.comment_more);
mProgressBar = findViewById(R.id.progress_bar);
@@ -163,18 +196,35 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
handleAnnouncer(getAnnouncer());
}
@Override
protected void onGetFitPaddingBottom(int b) {
mDetailFrameLayout.setPadding(0, 0, 0, b);
}
@Override
public void onClick(View v) {
GalleryDetail gd = mGalleryDetail;
if (gd == null) {
return;
}
if (v == mRead) {
} else if (v == mDownload) {
} else if (v == mFavorite) {
} else if (v == mTorrent) {
} else if (v == mShare) {
StartActivityHelper.share(getStageActivity(),
EhClient.getDetailUrl(Config.getEhSource(), gd.gid, gd.token, 0));
}
}
private String getRatingText(float rating) {
String undefine = "(´_ゝ`)";
if (rating == Float.NaN) {
if (Float.isNaN(rating)) {
return undefine;
}
@@ -216,6 +266,72 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
" (" + rating + ", " + ratedTimes + ")";
}
private void fillTags(List<TagGroup> tags) {
Context context = getStageActivity();
LayoutInflater inflater = getStageActivity().getLayoutInflater();
int x = UiUtils.dp2pix(context, 2);
int y = UiUtils.dp2pix(context, 4);
for (TagGroup tagGroup : tags) {
LinearLayout tagGroupLayout = new LinearLayout(context);
tagGroupLayout.setOrientation(LinearLayout.HORIZONTAL);
AutoWrapLayout tagLayout = new AutoWrapLayout(context);
// Group name
@SuppressLint("InflateParams")
TextView groupNameView = (TextView) inflater.inflate(R.layout.tag_group, null);
groupNameView.setText(tagGroup.groupName);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(x, y, x, y);
tagGroupLayout.addView(groupNameView, lp);
int count = tagGroup.getTagCount();
for (int i = 0; i < count; i++) {
@SuppressLint("InflateParams")
TextView tagView = (TextView) inflater.inflate(R.layout.tag, null);
tagView.setText(tagGroup.getTagAt(i));
/*
tagView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(GalleryDetailActivity.this,
GalleryListActivity.class);
intent.setAction(GalleryListActivity.ACTION_GALLERY_LIST);
intent.putExtra(GalleryListActivity.KEY_MODE,
ListUrls.MODE_TAG);
intent.putExtra(GalleryListActivity.KEY_TAG, groupName + ":" + tag);
intent.setAction(GalleryListActivity.ACTION_GALLERY_LIST);
startActivity(intent);
}
});
*/
AutoWrapLayout.LayoutParams alp = new AutoWrapLayout.LayoutParams();
alp.setMargins(x, y, x, y);
tagLayout.addView(tagView, alp);
}
tagGroupLayout.addView(tagLayout);
mTag.addView(tagGroupLayout);
}
}
private View createCommentView(Comment comment) {
View view = LayoutInflater.from(getStageActivity()).inflate(R.layout.item_comment, null);
((TextView) view.findViewById(R.id.user)).setText(comment.user);
((TextView) view.findViewById(R.id.time)).setText(comment.time);
TextView commentText = (TextView) view.findViewById(R.id.comment);
commentText.setMaxLines(3);
commentText.setAutoLinkMask(0);
URLImageGetter p = new URLImageGetter(
commentText, EhApplication.getConaco(getStageActivity()));
commentText.setText(Html.fromHtml(comment.comment, p, null));
return view;
}
private class RatingTextViewFactory implements ViewSwitcher.ViewFactory {
@Override
public View makeView() {
@@ -229,6 +345,8 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
@Override
public void onSuccess(GalleryDetail galleryDetail) {
mGalleryDetail = galleryDetail;
Resources resources = getStageActivity().getResources();
mLanguage.setText(galleryDetail.language);
mPages.setText(String.format(resources.getString(R.string.page_count),
@@ -240,12 +358,30 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener {
galleryDetail.favoredTimes));
mRating.setRating(galleryDetail.rating);
mRatingText.setText(getAllRatingText(galleryDetail.rating, galleryDetail.ratedTimes));
fillTags(galleryDetail.tags);
int commentNum = galleryDetail.comments.size();
if (commentNum == 0) {
mCommentMore.setText("暂无评论");
} else if (commentNum <= 2){
mCommentMore.setText("暂无更多评论");
} else {
mCommentMore.setText("更多评论");
}
int maxShown = Math.min(2, commentNum);
for (int i = 0; i < maxShown; i++) {
mComment.addView(createCommentView(galleryDetail.comments.get(i)), i,
new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
}
mViewTransition.showView(0);
}
@Override
public void onFailure(Exception e) {
e.printStackTrace();
// TODO
}
}
}
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2014 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.widget;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.NonNull;
import android.text.Layout;
import android.text.Spannable;
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.TextView;
public class LinkifyTextView extends TextView {
private String mUrl;
public LinkifyTextView(Context context) {
super(context);
init();
}
public LinkifyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LinkifyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public void init() {
// Just make urls striking
// LinkifyTextView itself should not handle touch url event
setAutoLinkMask(Linkify.WEB_URLS);
setLinksClickable(false);
}
public String getTouchedUrl() {
return mUrl;
}
/**
* Call it when you do not need the url any more
*/
public void clearTouchedUrl() {
mUrl = null;
}
@Override
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(@NonNull MotionEvent event) {
if (getText() instanceof Spannable) {
// Get this code from android.text.method.LinkMovementMethod.
// Work fine !
int x = (int) event.getX();
int y = (int) event.getY();
boolean isGetUrl = false;
x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
x += getScrollX();
y += getScrollY();
Layout layout = getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] links = ((Spannable)getText()).getSpans(off, off, ClickableSpan.class);
for (ClickableSpan link : links) {
if (link instanceof URLSpan) {
mUrl = ((URLSpan)link).getURL();
isGetUrl = true;
break;
}
}
if (!isGetUrl) {
mUrl = null;
}
} else {
mUrl = null;
}
return super.onTouchEvent(event);
}
}
@@ -0,0 +1,49 @@
package com.hippo.miscellaneous;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.widget.Toast;
import com.hippo.ehviewer.R;
public class StartActivityHelper {
public static boolean sendEmail(@NonNull Activity from, @NonNull String address,
@Nullable String subject, @Nullable String text) {
Intent i = new Intent(Intent.ACTION_SENDTO);
i.setData(Uri.parse("mailto:" + address));
if (subject != null) {
i.putExtra(Intent.EXTRA_SUBJECT, subject);
}
if (text != null) {
i.putExtra(Intent.EXTRA_TEXT, text);
}
try {
from.startActivity(i);
return true;
} catch (ActivityNotFoundException e) {
Toast.makeText(from, R.string.em_cant_find_activity, Toast.LENGTH_SHORT).show();
return false;
}
}
public static boolean share(@NonNull Activity from, String text) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, text);
sendIntent.setType("text/plain");
try {
from.startActivity(sendIntent);
return true;
} catch (ActivityNotFoundException e) {
Toast.makeText(from, R.string.em_cant_find_activity, Toast.LENGTH_SHORT).show();
return false;
}
}
}
@@ -0,0 +1,26 @@
package com.hippo.util;
import android.graphics.drawable.Drawable;
import android.text.Html;
import android.widget.TextView;
import com.hippo.conaco.Conaco;
import com.hippo.drawable.UnikeryDrawable;
public class URLImageGetter implements Html.ImageGetter {
private TextView mTextView;
private Conaco mConaco;
public URLImageGetter(TextView view, Conaco conaco) {
mTextView = view;
mConaco = conaco;
}
@Override
public Drawable getDrawable(String source) {
UnikeryDrawable drawable = new UnikeryDrawable(mTextView);
mConaco.load(drawable, source, source);
return drawable;
}
}
@@ -0,0 +1,286 @@
/*
* Copyright (C) 2014 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.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.hippo.ehviewer.R;
import java.util.ArrayList;
import java.util.List;
/**
* A ViewGroup that can layout views in line and auto wrap
*
* @author Hippo
*
*/
public class AutoWrapLayout extends ViewGroup {
private final static String TAG = "AutoWrapLayout";
private final List<Rect> rectList = new ArrayList<>();
private Alignment mAlignment;
private static final Alignment[] sBaseLineArray = { Alignment.TOP,
Alignment.CENTER, Alignment.BOTTOM };
public enum Alignment {
TOP(0),
CENTER(1),
BOTTOM(2);
Alignment(int ni) {
nativeInt = ni;
}
final int nativeInt;
}
public AutoWrapLayout(Context context) {
super(context);
}
public AutoWrapLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoWrapLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.AutoWrapLayout, defStyle, 0);
int index = a.getInt(R.styleable.AutoWrapLayout_alignment, -1);
if (index >= 0) {
setAlignment(sBaseLineArray[index]);
}
a.recycle();
}
public void setAlignment(Alignment baseLine) {
if (baseLine == null) {
return;
}
if (mAlignment != baseLine) {
mAlignment = baseLine;
requestLayout();
invalidate();
}
}
public Alignment getAlignment() {
return mAlignment;
}
private void adjustBaseLine(int lineHeight, int startIndex, int endIndex) {
if (mAlignment == Alignment.TOP)
return;
for (int index = startIndex; index < endIndex; index++) {
final View child = getChildAt(index);
final LayoutParams lp =
(LayoutParams)child.getLayoutParams();
Rect rect = rectList.get(index);
int offsetRaw = lineHeight - rect.height() - lp.topMargin - lp.bottomMargin;
if (mAlignment == Alignment.CENTER)
rect.offset(0, offsetRaw/2);
else if (mAlignment == Alignment.BOTTOM)
rect.offset(0, offsetRaw);
}
}
// TODO Take vertical mode
/**
* each row or line at least show one child
*
* horizontal only show child can show or partly show in parent
*/
@SuppressLint("DrawAllocation")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode == MeasureSpec.UNSPECIFIED)
maxWidth = Integer.MAX_VALUE;
if (heightMode == MeasureSpec.UNSPECIFIED)
maxHeight = Integer.MAX_VALUE;
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int maxRightBound = maxWidth - paddingRight;
int maxBottomBound = maxHeight - paddingBottom;
int left;
int top;
int right;
int bottom;
int rightBound = paddingLeft;
int maxRightNoPadding = rightBound;
int bottomBound;
int lastMaxBottom = paddingTop;
int maxBottom = lastMaxBottom;
int childWidth;
int childHeight;
int lineStartIndex = 0;
int lineEndIndex = 0; // endIndex + 1
rectList.clear();
int childCount = getChildCount();
for (int index = 0; index < childCount; index++) {
final View child = getChildAt(index);
child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
if (child.getVisibility() == View.GONE)
continue;
final LayoutParams lp =
(LayoutParams)child.getLayoutParams();
childWidth = child.getMeasuredWidth();
childHeight = child.getMeasuredHeight();
left = rightBound + lp.leftMargin;
right = left + childWidth;
rightBound = right + lp.rightMargin;
if (rightBound > maxRightBound) { // Go to next row
lineEndIndex = index;
// Adjust child position base on baseline
adjustBaseLine(maxBottom - lastMaxBottom, lineStartIndex, lineEndIndex);
// If child can't show in parent begin this line
if (maxBottom >= maxBottomBound)
break;
// If it is first item in line, try to show it all
if (lineEndIndex == lineStartIndex) {
child.measure(MeasureSpec.makeMeasureSpec(
maxWidth - paddingLeft - paddingRight
- lp.leftMargin - lp.rightMargin, MeasureSpec.AT_MOST),
MeasureSpec.UNSPECIFIED);
childWidth = child.getMeasuredWidth();
childHeight = child.getMeasuredHeight();
}
left = paddingLeft + lp.leftMargin;
right = left + childWidth;
rightBound = right + lp.rightMargin;
lastMaxBottom = maxBottom;
top = lastMaxBottom + lp.topMargin;
bottom = top + childHeight;
bottomBound = bottom + lp.bottomMargin;
lineStartIndex = index;
} else {
top = lastMaxBottom + lp.topMargin;
bottom = top + childHeight;
bottomBound = bottom + lp.bottomMargin;
}
// Update max
if (rightBound > maxRightNoPadding)
maxRightNoPadding = rightBound;
if (bottomBound > maxBottom)
maxBottom = bottomBound;
Rect rect = new Rect();
rect.left = left;
rect.top = top;
rect.right = right;
rect.bottom = bottom;
rectList.add(rect);
}
// Handle last line baseline
adjustBaseLine(maxBottom - lastMaxBottom, lineStartIndex, rectList.size());
int measuredWidth;
int measuredHeight;
if (widthMode == MeasureSpec.EXACTLY)
measuredWidth = maxWidth;
else
measuredWidth = maxRightNoPadding + paddingRight;
if (heightMode == MeasureSpec.EXACTLY)
measuredHeight = maxHeight;
else {
measuredHeight = maxBottom + paddingBottom;
if (heightMode == MeasureSpec.AT_MOST)
measuredHeight = measuredHeight > maxHeight ? maxHeight : measuredHeight;
}
setMeasuredDimension(measuredWidth, measuredHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = rectList.size();
for(int i = 0; i < count; i++){
final View child = this.getChildAt(i);
if (child.getVisibility() == View.GONE)
continue;
Rect rect = rectList.get(i);
child.layout(rect.left, rect.top, rect.right, rect.bottom);
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
public static class LayoutParams extends MarginLayoutParams {
public LayoutParams() {
this(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
}
}