Created
August 3, 2011 15:43
-
-
Save franciscojunior/1122947 to your computer and use it in GitHub Desktop.
Sample of viewflow with fragments. Note that you have to use the custom file TitleFlowIndicator below because it contains the support for the FragmentPagerAdapter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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"; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment