标签:滑动
最近又有大片上映了,前几天刚看完《末日崩塌》,《侏罗纪世界》又来了,对于大片迷来说是一种福利,所以这几天手机上装了各种电影票团购软件,没办法,同样的电影同样的电影院同样的座位,但是不同的团购软件,价格就不一样。ok,言归正传
在淘宝电影上面有这样一个功能,日期可以滑动,并且选中的是在正中间,效果如下:
看完了,那么问题来了。这个功能怎么实现呢?
我们先来分析一下:
把功能拆分一下来看,如果不能滚动,是不是很好实现?其实就是一个 tab 栏,我在前面的 blog 中Android 快速实现 ViewPager 滑动页卡切换(可用作整个 app上导航) 中就实现了此功能,然后在此功能的基础上加上滚动功能即可,具体的实现原理是通过水平滚动控件 HorizontalScrollView把 tab 栏包含起来,然后通过 tab 的选中item 来控制HorizontalScrollView的滚动。
代码实现:
1、实现 自定义 tab,这里就不细讲了,跟前面那篇 blog 几乎一样,直接贴代码了,不清楚的请看前面的 blog
package toolbar.scrollstripview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* Created by moon.zhong on 2015/5/25.
*/
public class SlidingTabView extends LinearLayout {
private static final float DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0.5f;
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3;
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
private final float mBottomBorderThickness;
private final Paint mBottomBorderPaint;
private final int mSelectedIndicatorThickness;
private final Paint mSelectedIndicatorPaint;
private final Paint mDividerPaint;
private final float mDividerHeight;
private int mSelectedPosition;
private float mSelectionOffset;
public SlidingTabView(Context context) {
this(context, null);
}
public SlidingTabView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
final float density = getResources().getDisplayMetrics().density;
TypedValue outValue = new TypedValue();
getContext().getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
final int themeForegroundColor = outValue.data;
mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
mBottomBorderPaint = new Paint();
mBottomBorderPaint.setColor(getResources().getColor(R.color.color_line));
mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
mSelectedIndicatorPaint = new Paint();
mSelectedIndicatorPaint.setColor(0xffff1322);
mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
mDividerPaint = new Paint();
mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
mDividerPaint.setColor(getResources().getColor(R.color.color_line));
}
void viewPagerChange(int position, float offset){
this.mSelectedPosition = position ;
this.mSelectionOffset = offset ;
if (offset == 0){
for (int i = 0; i < getChildCount(); i++) {
TextView child = (TextView) getChildAt(i);
child.setTextColor(0xff666666);
}
TextView selectedTitle = (TextView) getChildAt(mSelectedPosition);
selectedTitle.setTextColor(0xffff1322);
}
invalidate();
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
final int height = getHeight();
final int childCount = getChildCount();
final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
if (childCount > 0) {
TextView selectedTitle = (TextView) getChildAt(mSelectedPosition);
int left = selectedTitle.getLeft();
int right = selectedTitle.getRight();
selectedTitle.setTextColor(blendColors(0xff666666,0xffff1322,mSelectionOffset));
if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
TextView nextTitle = (TextView) getChildAt(mSelectedPosition + 1);
left = (int) (mSelectionOffset * nextTitle.getLeft() +
(1.0f - mSelectionOffset) * left);
right = (int) (mSelectionOffset * nextTitle.getRight() +
(1.0f - mSelectionOffset) * right);
nextTitle.setTextColor(blendColors(0xffff1322,0xff666666,mSelectionOffset));
}
canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
height, mSelectedIndicatorPaint);
}
canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
int separatorTop = (height - dividerHeightPx) / 2;
for (int i = 0; i < childCount - 1; i++) {
View child = getChildAt(i);
canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
separatorTop + dividerHeightPx, mDividerPaint);
}
}
private static int blendColors(int color1, int color2, float ratio) {
final float inverseRation = 1f - ratio;
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
return Color.rgb((int) r, (int) g, (int) b);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
2、自定义HorizontalScrollView
这个自定义类的功能,主要是填充 tab 的数据,通过选中的 item 来滚动HorizontalScrollView
填充数据:
private void populateTabStrip() {
final PagerAdapter adapter = mViewPager.getAdapter();
final OnClickListener tabClickListener = new TabClickListener();
/**/
/*通过 viewPager 的 item 来确定tab 的个数*/
for (int i = 0; i < adapter.getCount(); i++) {
View tabView = null;
TextView tabTitleView = null;
if (mTabViewLayoutId != 0) {
// If there is a custom tab view layout id set, try and inflate it
tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
false);
tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
}
if (tabView == null) {
/*创建textView*/
tabView = createDefaultTabView(getContext());
}
if (tabTitleView == null && TextView.class.isInstance(tabView)) {
tabTitleView = (TextView) tabView;
}
tabTitleView.setText(adapter.getPageTitle(i));
tabView.setOnClickListener(tabClickListener);
tabView.setBackgroundResource(R.drawable.item_selector_bg);
/*把 textView 放入到自定义的 tab 栏中*/
mTabStrip.addView(tabView);
}
}
滚动 ScrollView
/**
* 这个方法是关键
* 滚动 scrollview
* @param tabIndex
* @param positionOffset
*/
private void scrollToTab(int tabIndex, int positionOffset) {
final int tabStripChildCount = mTabStrip.getChildCount();
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
return;
}
/*获取当前选中的 item*/
View selectedChild = mTabStrip.getChildAt(tabIndex);
if (selectedChild != null) {
/*获取当前 item 的偏移量*/
int targetScrollX = selectedChild.getLeft() + positionOffset;
/*item 的宽度*/
int width = selectedChild.getWidth();
/*item 距离正中间的偏移量*/
mTitleOffset = (int) ((mWidth-width)/2.0f);
if (tabIndex > 0 || positionOffset > 0) {
/*计算出正在的偏移量*/
targetScrollX -= mTitleOffset;
}
Log.v("zgy","==================mWidth======="+mWidth) ;
/*这个时候偏移的量就是屏幕的正中间*/
scrollTo(targetScrollX, 0);
}
}
具体的调用当然就是 在 ViewPager 的OnPageChangeListener中。
完整类的代码如下:
package toolbar.scrollstripview;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.TextView;
/**
* Created by moon.zhong.
*/
public class SlidingTabLayout extends HorizontalScrollView {
private static final int TITLE_OFFSET_DIPS = 24;
private static final int TAB_VIEW_PADDING_DIPS_TB = 15;
private static final int TAB_VIEW_PADDING_DIPS = 16;
private static final int TAB_VIEW_TEXT_SIZE_SP = 14;
private int mTitleOffset;
private int mTabViewLayoutId;
private int mTabViewTextViewId;
private ViewPager mViewPager;
private final SlidingTabView mTabStrip;
private int mWidth ;
public SlidingTabLayout(Context context) {
this(context, null);
}
public SlidingTabLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setHorizontalScrollBarEnabled(false);
setFillViewport(true);
mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
mTabStrip = new SlidingTabView(context);
addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
DisplayMetrics displayMetrics = new DisplayMetrics() ;
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
mWidth = (int) (displayMetrics.widthPixels) ;
}
public void setViewPager(ViewPager viewPager) {
mTabStrip.removeAllViews();
mViewPager = viewPager;
if (viewPager != null) {
viewPager.addOnPageChangeListener(new InternalViewPagerListener());
populateTabStrip();
}
}
private void populateTabStrip() {
final PagerAdapter adapter = mViewPager.getAdapter();
final OnClickListener tabClickListener = new TabClickListener();
/**/
/*通过 viewPager 的 item 来确定tab 的个数*/
for (int i = 0; i < adapter.getCount(); i++) {
View tabView = null;
TextView tabTitleView = null;
if (mTabViewLayoutId != 0) {
// If there is a custom tab view layout id set, try and inflate it
tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
false);
tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
}
if (tabView == null) {
/*创建textView*/
tabView = createDefaultTabView(getContext());
}
if (tabTitleView == null && TextView.class.isInstance(tabView)) {
tabTitleView = (TextView) tabView;
}
tabTitleView.setText(adapter.getPageTitle(i));
tabView.setOnClickListener(tabClickListener);
tabView.setBackgroundResource(R.drawable.item_selector_bg);
/*把 textView 放入到自定义的 tab 栏中*/
mTabStrip.addView(tabView);
}
}
/*这里就是创建 textView,没什么可讲的*/
protected TextView createDefaultTabView(Context context) {
TextView textView = new TextView(context);
textView.setGravity(Gravity.CENTER);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
TypedValue outValue = new TypedValue();
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
outValue, true);
textView.setBackgroundResource(outValue.resourceId);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
textView.setAllCaps(true);
}
int paddingTB = (int) (TAB_VIEW_PADDING_DIPS_TB * getResources().getDisplayMetrics().density);
textView.setPadding(0, paddingTB, 0, paddingTB);
textView.setTextColor(0xff666666);
int width = (int) (100 * getResources().getDisplayMetrics().density);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT) ;
textView.setLayoutParams(params);
return textView;
}
/**
* 这个方法是关键
* 滚动 scrollview
* @param tabIndex
* @param positionOffset
*/
private void scrollToTab(int tabIndex, int positionOffset) {
final int tabStripChildCount = mTabStrip.getChildCount();
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
return;
}
/*获取当前选中的 item*/
View selectedChild = mTabStrip.getChildAt(tabIndex);
if (selectedChild != null) {
/*获取当前 item 的偏移量*/
int targetScrollX = selectedChild.getLeft() + positionOffset;
/*item 的宽度*/
int width = selectedChild.getWidth();
/*item 距离正中间的偏移量*/
mTitleOffset = (int) ((mWidth-width)/2.0f);
if (tabIndex > 0 || positionOffset > 0) {
/*计算出正在的偏移量*/
targetScrollX -= mTitleOffset;
}
Log.v("zgy","==================mWidth======="+mWidth) ;
/*这个时候偏移的量就是屏幕的正中间*/
scrollTo(targetScrollX, 0);
}
}
private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
private int mScrollState;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
int tabStripChildCount = mTabStrip.getChildCount();
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
return;
}
mTabStrip.viewPagerChange(position, positionOffset);
View selectedTitle = mTabStrip.getChildAt(position);
int extraOffset = (selectedTitle != null)
? (int) (positionOffset * selectedTitle.getWidth())
: 0;
scrollToTab(position, extraOffset);
}
@Override
public void onPageScrollStateChanged(int state) {
mScrollState = state;
}
@Override
public void onPageSelected(int position) {
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
mTabStrip.viewPagerChange(position, 0f);
scrollToTab(position, 0);
}
}
}
private class TabClickListener implements OnClickListener {
@Override
public void onClick(View v) {
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
if (v == mTabStrip.getChildAt(i)) {
mViewPager.setCurrentItem(i);
return;
}
}
}
}
}
代码引用
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<toolbar.scrollstripview.SlidingTabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/id_sliding_view"/>
<android.support.v4.view.ViewPager
android:id="@+id/id_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/id_sliding_view"/>
</RelativeLayout>
运行效果:
总结:
1、对前面Android 快速实现 ViewPager 滑动页卡切换(可用作整个 app上导航) blog 的运用;
2、scrollTo(targetScrollX, 0);方法的灵活运用,targetScrollX如果小于0,获取targetScrollX大于 view 的宽度的时候这个方法都不会起作用,看源码可知:
public void scrollTo(int x, int y) {
// we rely on the fact the View.scrollBy calls scrollTo.
if (getChildCount() > 0) {
View child = getChildAt(0);
x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth());
y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight());
if (x != mScrollX || y != mScrollY) {
super.scrollTo(x, y);
}
}
}
对 x、y 做了相应的截取操作
标签:滑动
原文地址:http://blog.csdn.net/jxxfzgy/article/details/46462865