public
Last active

Sample of viewflow with fragments. Note that you have to use the custom file TitleFlowIndicator below because it contains the support for the FragmentPagerAdapter

  • Download Gist
TitleFlowIndicator.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
/*
* Copyright (C) 2011 Patrik Ã…kerfeldt
*
* 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 org.taptwo.android.widget;
 
import java.util.ArrayList;
 
import org.taptwo.android.widget.viewflow.R;
 
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.support.v4.view.PagerAdapter;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
 
/**
* A TitleFlowIndicator is a FlowIndicator which displays the title of left view
* (if exist), the title of the current select view (centered) and the title of
* the right view (if exist). When the user scrolls the ViewFlow then titles are
* also scrolled.
*
*/
public class TitleFlowIndicator extends TextView implements FlowIndicator {
 
private static final int TITLE_PADDING = 10;
private static final int SELECTED_COLOR = 0xFFFFC445;
private static final int TEXT_COLOR = 0xFFAAAAAA;
private static final int TEXT_SIZE = 15;
private static final int FOOTER_LINE_HEIGHT = 4;
private static final int FOOTER_COLOR = 0xFFFFC445;
private static final int FOOTER_TRIANGLE_HEIGHT = 10;
private ViewFlow viewFlow;
private int currentScroll = 0;
private TitleProvider titleProvider = null;
private int currentPosition = 0;
private Paint paintText;
private Paint paintSelected;
private Path path;
private Paint paintFooterLine;
private Paint paintFooterTriangle;
private int footerTriangleHeight;
private int titlePadding;
private int footerLineHeight;
private PagerAdapter pagerAdapter;
 
/**
* Default constructor
*/
public TitleFlowIndicator(Context context) {
super(context);
initDraw(TEXT_COLOR, TEXT_SIZE, SELECTED_COLOR, FOOTER_LINE_HEIGHT, FOOTER_COLOR);
}
 
/**
* The contructor used with an inflater
*
* @param context
* @param attrs
*/
public TitleFlowIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
// Retrieve styles attributs
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TitleFlowIndicator);
// Retrieve the colors to be used for this view and apply them.
int footerColor = a.getColor(R.styleable.TitleFlowIndicator_footerColor, FOOTER_COLOR);
footerLineHeight = a.getInt(R.styleable.TitleFlowIndicator_footerLineHeight, FOOTER_LINE_HEIGHT);
footerTriangleHeight = a.getInt(R.styleable.TitleFlowIndicator_footerTriangleHeight, FOOTER_TRIANGLE_HEIGHT);
int selectedColor = a.getColor(R.styleable.TitleFlowIndicator_selectedColor, SELECTED_COLOR);
int textColor = a.getColor(R.styleable.TitleFlowIndicator_textColor, TEXT_COLOR);
float textSize = a.getFloat(R.styleable.TitleFlowIndicator_textSize, TEXT_SIZE);
titlePadding = a.getInt(R.styleable.TitleFlowIndicator_titlePadding, TITLE_PADDING);
initDraw(textColor, textSize, selectedColor, footerLineHeight, footerColor);
}
 
/**
* Initialize draw objects
*/
private void initDraw(int textColor, float textSize, int selectedColor, int footerLineHeight, int footerColor) {
paintText = new Paint();
paintText.setColor(textColor);
paintText.setTextSize(textSize);
paintText.setAntiAlias(true);
paintSelected = new Paint();
paintSelected.setColor(selectedColor);
paintSelected.setTextSize(textSize);
paintSelected.setAntiAlias(true);
paintFooterLine = new Paint();
paintFooterLine.setStyle(Paint.Style.FILL_AND_STROKE);
paintFooterLine.setStrokeWidth(footerLineHeight);
paintFooterLine.setColor(FOOTER_COLOR);
paintFooterTriangle = new Paint();
paintFooterTriangle.setStyle(Paint.Style.FILL_AND_STROKE);
paintFooterTriangle.setColor(footerColor);
}
 
/*
* (non-Javadoc)
*
* @see android.view.View#onDraw(android.graphics.Canvas)
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
 
// Calculate views bounds
ArrayList<Rect> bounds = calculateAllBounds(paintText);
// If no value then add a fake one
//int count = (viewFlow != null && viewFlow.getAdapter() != null) ? viewFlow.getAdapter().getCount() : 1;
int count = (pagerAdapter != null) ? pagerAdapter.getCount() : 1;
// Verify if the current view must be clipped to the screen
Rect curViewBound = bounds.get(currentPosition);
int curViewWidth = curViewBound.right - curViewBound.left;
if (curViewBound.left < 0) {
// Try to clip to the screen (left side)
curViewBound.left = 0;
curViewBound.right = curViewWidth;
}
if (curViewBound.right > getLeft() + getWidth()) {
// Try to clip to the screen (right side)
curViewBound.right = getLeft() + getWidth();
curViewBound.left = curViewBound.right - curViewWidth;
}
// Left views starting from the current position
if (currentPosition > 0) {
for (int iLoop = currentPosition - 1; iLoop >= 0; iLoop--) {
Rect bound = bounds.get(iLoop);
int w = bound.right - bound.left;
// Si left side is outside the screen
if (bound.left < 0) {
// Try to clip to the screen (left side)
bound.left = 0;
bound.right = w;
// Except if there's an intersection with the right view
if (iLoop < count - 1 && currentPosition != iLoop) {
Rect rightBound = bounds.get(iLoop + 1);
// Intersection
if (bound.right + TITLE_PADDING > rightBound.left) {
bound.left = rightBound.left - (w + titlePadding);
}
}
}
}
}
// Right views starting from the current position
if (currentPosition < count - 1) {
for (int iLoop = currentPosition + 1 ; iLoop < count; iLoop++) {
Rect bound = bounds.get(iLoop);
int w = bound.right - bound.left;
// If right side is outside the screen
if (bound.right > getLeft() + getWidth()) {
// Try to clip to the screen (right side)
bound.right = getLeft() + getWidth();
bound.left = bound.right - w;
// Except if there's an intersection with the left view
if (iLoop > 0 && currentPosition != iLoop) {
Rect leftBound = bounds.get(iLoop - 1);
// Intersection
if (bound.left - TITLE_PADDING < leftBound.right) {
bound.left = leftBound.right + titlePadding;
}
}
}
}
}
 
// Now draw views
for (int iLoop = 0; iLoop < count; iLoop++) {
// Get the title
String title = getTitle(iLoop);
Rect bound = bounds.get(iLoop);
// Only if one side is visible
if ((bound.left > getLeft() && bound.left < getLeft() + getWidth()) || (bound.right > getLeft() && bound.right < getLeft() + getWidth())) {
Paint paint = paintText;
// Change the color is the title is closed to the center
int middle = (bound.left + bound.right) / 2;
if (Math.abs(middle - (getWidth() / 2)) < 20) {
paint = paintSelected;
}
canvas.drawText(title, bound.left, bound.bottom, paint);
}
}
 
// Draw the footer line
path = new Path();
path.moveTo(0, getHeight()-footerLineHeight);
path.lineTo(getWidth(), getHeight()-footerLineHeight);
path.close();
canvas.drawPath(path, paintFooterLine);
// Draw the footer triangle
path = new Path();
path.moveTo(getWidth() / 2, getHeight()-footerLineHeight-footerTriangleHeight);
path.lineTo(getWidth() / 2 + footerTriangleHeight, getHeight()-footerLineHeight);
path.lineTo(getWidth() / 2 - footerTriangleHeight, getHeight()-footerLineHeight);
path.close();
canvas.drawPath(path, paintFooterTriangle);
 
}
 
/**
* Calculate views bounds and scroll them according to the current index
*
* @param paint
* @param currentIndex
* @return
*/
private ArrayList<Rect> calculateAllBounds(Paint paint) {
ArrayList<Rect> list = new ArrayList<Rect>();
// For each views (If no values then add a fake one)
//int count = (viewFlow != null && viewFlow.getAdapter() != null) ? viewFlow.getAdapter().getCount() : 1;
int count = (pagerAdapter != null) ? pagerAdapter.getCount() : 1;
for (int iLoop = 0; iLoop < count; iLoop++) {
Rect bounds = calcBounds(iLoop, paint);
int w = (bounds.right - bounds.left);
int h = (bounds.bottom - bounds.top);
bounds.left = (getWidth() / 2) - (w / 2) - currentScroll + (iLoop * getWidth());
bounds.right = bounds.left + w;
bounds.top = 0;
bounds.bottom = h;
list.add(bounds);
}
 
return list;
}
 
/**
* Calculate the bounds for a view's title
*
* @param index
* @param paint
* @return
*/
private Rect calcBounds(int index, Paint paint) {
// Get the title
String title = getTitle(index);
// Calculate the text bounds
Rect bounds = new Rect();
bounds.right = (int) paint.measureText(title);
bounds.bottom = (int) (paint.descent()-paint.ascent());
return bounds;
}
 
/**
* Returns the title
*
* @param pos
* @return
*/
private String getTitle(int pos) {
// Set the default title
String title = "title " + pos;
// If the TitleProvider exist
if (titleProvider != null) {
title = titleProvider.getTitle(pos);
}
return title;
}
 
/*
* (non-Javadoc)
*
* @see org.taptwo.android.widget.FlowIndicator#onScrolled(int, int, int,
* int)
*/
public void onScrolled(int h, int v, int oldh, int oldv) {
currentScroll = h;
invalidate();
}
 
/*
* (non-Javadoc)
*
* @see
* org.taptwo.android.widget.ViewFlow.ViewSwitchListener#onSwitched(android
* .view.View, int)
*/
public void onSwitched(View view, int position) {
currentPosition = position;
invalidate();
}
 
/*
* (non-Javadoc)
*
* @see
* org.taptwo.android.widget.FlowIndicator#setViewFlow(org.taptwo.android
* .widget.ViewFlow)
*/
public void setViewFlow(ViewFlow view) {
viewFlow = view;
invalidate();
}
public void setPagerAdapter(PagerAdapter pager) {
pagerAdapter = pager;
}
 
/**
* Set the title provider
*
* @param provider
*/
public void setTitleProvider(TitleProvider provider) {
titleProvider = provider;
}
/*
* (non-Javadoc)
*
* @see android.view.View#onMeasure(int, int)
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}
 
/**
* Determines the width of this view
*
* @param measureSpec
* A measureSpec packed into an int
* @return The width of the view, honoring constraints from measureSpec
*/
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
 
if (specMode != MeasureSpec.EXACTLY) {
throw new IllegalStateException(
"ViewFlow can only be used in EXACTLY mode.");
}
result = specSize;
return result;
}
 
/**
* Determines the height of this view
*
* @param measureSpec
* A measureSpec packed into an int
* @return The height of the view, honoring constraints from measureSpec
*/
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
 
// We were told how big to be
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
}
// Measure the height
else {
// Calculate the text bounds
Rect bounds = new Rect();
bounds.bottom = (int) (paintText.descent()-paintText.ascent());
result = bounds.bottom - bounds.top + footerTriangleHeight + footerLineHeight + 10;
return result;
}
return result;
}
}
ViewFlowFragmentsActivity.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
/*
* Copyright (C) 2011 Francisco Figueiredo Jr.
*
* 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 test.viewflowfragments;
 
import org.taptwo.android.widget.TitleFlowIndicator;
import org.taptwo.android.widget.TitleProvider;
 
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.ListFragment;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
 
public class ViewFlowFragmentsActivity extends FragmentActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ViewPager mViewPager = (ViewPager) findViewById(R.id.pager);
TitleFlowIndicator tfi = (TitleFlowIndicator) findViewById(R.id.viewflowindic);
final TextIndicatorAdapter a = new TextIndicatorAdapter(this, mViewPager, tfi);
//a.setCurrentView(0);
tfi.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
a.setCurrentView(2);
}
});
}
public static final String[] TITLES =
{
"Henry IV (1)",
"Henry V",
"Henry VIII",
"Richard II",
"Richard III",
"Merchant of Venice",
"Othello",
"King Lear"
};
public static class ArrayListFragment extends ListFragment {
 
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, TITLES));
}
 
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Log.i("FragmentList", "Item clicked: " + id);
}
}
 
public static class TextIndicatorAdapter extends FragmentPagerAdapter
implements ViewPager.OnPageChangeListener, TitleProvider {
 
public TextIndicatorAdapter(FragmentActivity activity, ViewPager pager,
TitleFlowIndicator tfi) {
super(activity.getSupportFragmentManager());
 
mContext = activity;
mViewPager = pager;
mFlowIndicator = tfi;
 
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
 
tfi.setTitleProvider(this);
tfi.setPagerAdapter(this);
 
// TODO Auto-generated constructor stub
}
 
private final Context mContext;
 
private final ViewPager mViewPager;
 
private final TitleFlowIndicator mFlowIndicator;
 
 
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
// TODO Auto-generated method stub
 
int hPerceived = positionOffsetPixels + position
* mViewPager.getWidth();
 
mFlowIndicator.onScrolled(hPerceived, 0, 0, 0);
// Log.d("viewpager", String.format("%d %d %d %f %d",
// mViewPager.getWidth(), hPerceived, position,
// positionOffset, positionOffsetPixels));
 
}
 
@Override
public void onPageSelected(int position) {
// TODO Auto-generated method stub
 
mFlowIndicator.onSwitched(null, position);
Log.d("viewpager", String.valueOf(position));
 
}
 
@Override
public void onPageScrollStateChanged(int state) {
// TODO Auto-generated method stub
 
}
 
@Override
public Fragment getItem(int position) {
// TODO Auto-generated method stub
if (position == 0)
return Fragment.instantiate(mContext,
ArrayListFragment.class.getName(), null);
else
return Fragment.instantiate(mContext,
ArrayListFragment.class.getName(), null);
}
 
@Override
public int getCount() {
// TODO Auto-generated method stub
return 2;
}
 
public void setCurrentView(int id) {
 
mViewPager.setCurrentItem(id);
}
 
@Override
public String getTitle(int position) {
// TODO Auto-generated method stub
if (position == 0)
return "List 1";
else
return "List 2";
}
 
}
 
}
main.xml
XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
<?xml version="1.0" encoding="utf-8"?>
 
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/test.viewflowfragments"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:layout_width="fill_parent"
android:gravity="center_horizontal" android:id="@+id/header_layout"
android:orientation="vertical" android:layout_height="wrap_content">
<org.taptwo.android.widget.TitleFlowIndicator
android:id="@+id/viewflowindic" android:layout_height="wrap_content"
android:layout_width="fill_parent"
app:footerLineHeight="1"
app:footerTriangleHeight="8" app:textColor="#FFFFFFFF" app:selectedColor="#FFFFC445" app:footerColor="#FFFFC445" app:titlePadding="5" app:textSize="20" android:layout_marginTop="5dip" ></org.taptwo.android.widget.TitleFlowIndicator>
 
</LinearLayout>
 
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
 
</LinearLayout>

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.