/* Hacked based on android.support.v4.view.PagerTitleStrip.
* Which is Apache 2 license.
*/
package android.support.v4.view;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.vaguehope.onosendai.R;
public class ColumnTitleStrip extends ViewGroup implements ViewPager.Decor /* Decor is package-private :( */{
public interface ColumnClickListener {
void onColumnTitleClick (int position);
}
private static final int[] ATTRS = new int[] {
android.R.attr.textSize
};
private final int textSizePx;
private final PageListener mPageListener = new PageListener();
private ViewPager pager;
private WeakReference<PagerAdapter> mWatchingAdapter;
private ColumnClickListener columnClickListener;
private int mLastKnownCurrentPage = -1;
private float mLastKnownPositionOffset = -1;
private boolean mUpdatingText;
private boolean mUpdatingPositions;
private final List<TextView> labels = new ArrayList<TextView>();
private float relativePageWidth = 1.f;
public ColumnTitleStrip (final Context context) {
this(context, null);
}
public ColumnTitleStrip (final Context context, final AttributeSet attrs) {
super(context, attrs);
final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
this.textSizePx = a.getDimensionPixelSize(1, 0);
a.recycle();
}
public void setViewPager (final ViewPager newPager) {
if (this.pager != null) {
updateAdapter(this.pager.getAdapter(), null);
this.pager.setInternalPageChangeListener(null);
this.pager.setOnAdapterChangeListener(null);
}
this.pager = newPager;
if (this.pager != null) {
this.pager.setInternalPageChangeListener(this.mPageListener);
this.pager.setOnAdapterChangeListener(this.mPageListener);
updateAdapter(this.mWatchingAdapter != null ? this.mWatchingAdapter.get() : null, this.pager.getAdapter());
}
}
public void setColumnClickListener (final ColumnClickListener listener) {
this.columnClickListener = listener;
}
/**
* Null title to clear.
*/
public void setTempColumnTitle(final int position, final String title) {
final TextView tv = this.labels.get(position);
if (tv == null) return;
tv.setText(title != null ? title : String.valueOf(tv.getTag()));
}
@Override
protected void onDetachedFromWindow () {
super.onDetachedFromWindow();
setViewPager(null);
}
/**
* TODO may be able to factor out this not poorly-named method.
*/
void updateText (final int currentItem) {
this.mUpdatingText = true;
this.mLastKnownCurrentPage = currentItem;
if (!this.mUpdatingPositions) updateTextPositions(currentItem, this.mLastKnownPositionOffset, false);
this.mUpdatingText = false;
}
@Override
public void requestLayout () {
if (!this.mUpdatingText) super.requestLayout();
}
void updateAdapter (final PagerAdapter oldAdapter, final PagerAdapter newAdapter) {
if (oldAdapter != null) {
oldAdapter.unregisterDataSetObserver(this.mPageListener);
this.mWatchingAdapter = null;
}
if (newAdapter != null) {
newAdapter.registerDataSetObserver(this.mPageListener);
this.mWatchingAdapter = new WeakReference<PagerAdapter>(newAdapter);
createWidgets(newAdapter);
}
if (this.pager != null) {
this.mLastKnownCurrentPage = -1;
this.mLastKnownPositionOffset = -1;
updateText(this.pager.getCurrentItem());
requestLayout();
}
}
private void createWidgets (final PagerAdapter adapter) {
this.relativePageWidth = adapter.getPageWidth(Integer.MIN_VALUE); // TODO for now assume all columns same width.
if (this.labels.size() < adapter.getCount()) {
LayoutInflater inflater = null;
while (this.labels.size() < adapter.getCount()) {
if (inflater == null) inflater = LayoutInflater.from(getContext());
final TextView tv = (TextView) inflater.inflate(R.layout.titlestripbutton, this, false);
if (this.textSizePx != 0) tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, this.textSizePx);
tv.setOnClickListener(new TitleClickListener(this, this.labels.size()));
this.labels.add(tv);
addView(tv);
}
}
else if (this.labels.size() > adapter.getCount()) {
while (this.labels.size() > adapter.getCount()) {
removeView(this.labels.remove(this.labels.size() - 1));
}
}
final int width = getWidth() - getPaddingLeft() - getPaddingRight(); // TODO FIXME take into account getPageWidth().
final int childHeight = getHeight() - getPaddingTop() - getPaddingBottom();
final int childWidthSpec = MeasureSpec.makeMeasureSpec((int) (width * 0.8f), MeasureSpec.AT_MOST);
final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST);
for (int i = 0; i < this.labels.size(); i++) {
final TextView tv = this.labels.get(i);
final CharSequence title = adapter.getPageTitle(i);
tv.setText(title);
tv.setTag(title);
tv.measure(childWidthSpec, childHeightSpec);
}
}
void updateTextPositions (final int position, final float positionOffset, final boolean force) {
if (position != this.mLastKnownCurrentPage) {
updateText(position);
}
else if (!force && positionOffset == this.mLastKnownPositionOffset) {
return;
}
this.mUpdatingPositions = true;
// XXX Use pager's width.
final int contentWidth = this.pager.getWidth() - getPaddingLeft() - getPaddingRight();
final int columnWidth = (int) (contentWidth * this.relativePageWidth); // FIXME rounding error may be introduced.
final int firstColumnLeft = 0 - (columnWidth * position) - getLeft(); // XXX Offset Left.
for (int i = 0; i < this.labels.size(); i++) {
final TextView tv = this.labels.get(i);
final int tLeft = firstColumnLeft + (columnWidth * i) - (int) (positionOffset * columnWidth);
final int bottomGravTop = getHeight() - getPaddingBottom() - tv.getMeasuredHeight();
tv.layout(tLeft, bottomGravTop, tLeft + columnWidth, bottomGravTop + tv.getMeasuredHeight());
}
this.mLastKnownPositionOffset = positionOffset;
this.mUpdatingPositions = false;
}
@Override
protected void onMeasure (final int widthMeasureSpec, final int heightMeasureSpec) {
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode != MeasureSpec.EXACTLY) throw new IllegalStateException("Must measure with an exact width.");
// XXX Use pager's width.
final int contentWidth = this.pager.getWidth() - getPaddingLeft() - getPaddingRight();
final int columnWidth = (int) (contentWidth * this.relativePageWidth); // FIXME rounding error may be introduced.
final int vPadding = getPaddingTop() + getPaddingBottom();
final int childHeight = heightSize - vPadding;
final int childWidthSpec = MeasureSpec.makeMeasureSpec(columnWidth, MeasureSpec.EXACTLY);
final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST);
// Cascade measuring.
int maxTextHeight = 1;
for (final TextView tv : this.labels) {
tv.measure(childWidthSpec, childHeightSpec);
maxTextHeight = Math.max(maxTextHeight, tv.getMeasuredHeight());
}
if (heightMode == MeasureSpec.EXACTLY) {
setMeasuredDimension(widthSize, heightSize);
}
else {
setMeasuredDimension(widthSize, Math.max(getMinHeight(), maxTextHeight + vPadding));
}
}
@Override
protected void onLayout (final boolean changed, final int l, final int t, final int r, final int b) {
if (this.pager != null) {
final float offset = this.mLastKnownPositionOffset >= 0 ? this.mLastKnownPositionOffset : 0;
updateTextPositions(this.mLastKnownCurrentPage, offset, true);
}
}
int getMinHeight () {
int minHeight = 0;
final Drawable bg = getBackground();
if (bg != null) {
minHeight = bg.getIntrinsicHeight();
}
return minHeight;
}
private class PageListener extends DataSetObserver implements ViewPager.OnPageChangeListener, ViewPager.OnAdapterChangeListener {
@Override
public void onPageScrolled (final int position, final float positionOffset, final int positionOffsetPixels) {
updateTextPositions(position, positionOffset, false);
}
@Override
public void onPageSelected (final int position) {/* Unused. */}
@Override
public void onPageScrollStateChanged (final int state) {/* Unused */}
@Override
public void onAdapterChanged (final PagerAdapter oldAdapter, final PagerAdapter newAdapter) {
updateAdapter(oldAdapter, newAdapter);
}
@Override
public void onChanged () {
updateText(ColumnTitleStrip.this.pager.getCurrentItem());
final float offset = ColumnTitleStrip.this.mLastKnownPositionOffset >= 0 ? ColumnTitleStrip.this.mLastKnownPositionOffset : 0;
updateTextPositions(ColumnTitleStrip.this.pager.getCurrentItem(), offset, true);
}
}
private static class TitleClickListener implements OnClickListener {
private final ColumnTitleStrip host;
private final int position;
public TitleClickListener (final ColumnTitleStrip host, final int position) {
this.host = host;
this.position = position;
}
@Override
public void onClick (final View v) {
this.host.columnClickListener.onColumnTitleClick(this.position);
}
}
}