編輯:關於Android編程
最近又有大片上映了,前幾天剛看完《末日崩塌》,《侏羅紀世界》又來了,對於大片迷來說是一種福利,所以這幾天手機上裝了各種電影票團購軟件,沒辦法,同樣的電影同樣的電影院同樣的座位,但是不同的團購軟件,價格就不一樣。ok,言歸正傳
在淘寶電影上面有這樣一個功能,日期可以滑動,並且選中的是在正中間,效果如下:
看完了,那麼問題來了。這個功能怎麼實現呢?
我們先來分析一下:
把功能拆分一下來看,如果不能滾動,是不是很好實現?其實就是一個 tab 欄,我在前面的 blog 中Android 快速實現 ViewPager 滑動頁卡切換(可用作整個 app上導航) 中就實現了此功能,然後在此功能的基礎上加上滾動功能即可,具體的實現原理是通過水平滾動控件 HorizontalScrollView把 tab 欄包含起來,然後通過 tab 的選中item 來控制HorizontalScrollView的滾動。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPrT6wuvKtc/Wo7o8L3A+DQo8cD4xoaLKtc/WINfUtqjS5SB0YWKjrNXiwO++zbK7z7i9ssHLo6y4+seww+bEx8aqIGJsb2cgvLi69dK70fmjrNaxvdPM+bT6wuvBy6OssrvH5bP+tcTH67+0x7DD5rXEIGJsb2c8L3A+DQo8cHJlIGNsYXNzPQ=="brush:java;">
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;
}
}
}
}
}
代碼引用
運行效果:
總結:
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 做了相應的截取操作
1 功能描述類似王者榮耀,按下的技能如果是需要預判的或者是可以選擇單一目標,產生一個搖桿,在地形上顯示輔助的UI提示。存在以下幾種情況:1.扇形范圍技能 2.方
前言上一節我們實現了一個簡單的紋理映射的例子——一個簡單的貼圖,這節我們來做一些稍微復雜一點的例子,最後再給我們前面的立方體做一個紋理。 
https://github.com/daimajia/AndroidViewHover import android.support.v8.renderscrip
效果圖一、繪制圓環圓環故名思意,第一個首先繪制是圓環1:圓環繪制函數圓環APIpublic void drawArc (RectF oval, float startAn
Dagger 是一種android平台的依賴注入框架,是有一家專注於移動