自定義View指示器讓指示器下劃線和文字對齊
指示器的標籤分為兩種情形,一種是固定的一般3~5個標籤,另一種的移動的標籤;開發中經常會使用MD風格的TabLayout控制元件,這個控制元件什麼都很好,就是指示器的下滑線和文字不能夠對齊,這一點用過的同學都會知道的,我也在網上找了好久,也找到不到現成的,只好自己動手來寫一個啦!
先宣告一下,我是吧指示器標籤分兩種情形來寫的,固定和不固定,沒有合併,因為不是繼承同一個控制元件;另外要多看看TabLayout的原始碼,對於自定義這個控制元件很有好處,可以多多借鑑裡面的實現;
最下面可以下載程式碼,有問題可以留言交流;
說了這麼多先看看固定的標籤的效果:(Tab是固定的效果)
這種效果實現的主要步驟思路:
因為這種標籤固定
1.首先要把所有的標籤文字全部放到一個容器裡面,寬度按個數均分,容器選水平LinearLayout
2.要關聯上一個ViewPager,並根據ViewPager的改變動態繪製指示器;
新增的程式碼如下:
/**
* 把資料集合設定成Tab標籤
*/
public void setTabTitle(List<String> list) {
if (list == null)
return;
mTabTitleList = list;//tab資料來源
mTabCount = list.size();//tab的個數
for (int i = 0; i < list.size(); i++) {
String text = list.get(i);
addView(generateText(text));//新增到容器中
}
setTabOnClickListener();//設定點選事件
}
/**
* 自動生成TextView
*/
public TextView generateText(String text) {
TextView textView = new TextView(getContext());
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSize);
textView.setTextColor(mNormalColor);
textView.setGravity(Gravity.CENTER);
textView.setText(text);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0,
LinearLayout.LayoutParams.MATCH_PARENT, 1);
textView.setLayoutParams(lp);
return textView;
}
/**
* 每一個tab的點選事件
*/
public void setTabOnClickListener() {
if (mTabTitleList != null) {
for (int i = 0; i < mTabCount; i++) {
TextView textView = (TextView) getChildAt(i);
textView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = indexOfChild(v);
if (mViewPager != null)
mViewPager.setCurrentItem(index, true);
}
});
}
}
}
上面程式碼很簡單就是把根據文字生成了TextView新增到LinearLayout裡面,並且均分;這一步設定好了以後,下面就開始關聯ViewPager;
private RectF rectF = new RectF();//要繪製的矩形
//我們是根據下面兩個引數來動態計算並繪製
private int position;//ViewPager的裡面的position
private float ration;//滑動的百分比
/**
* 關聯到ViewPager
*/
public void setViewPager(ViewPager viewPager) {
this.mViewPager = viewPager;
this.mViewPager.addOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int pos) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
position = arg0;
ration = arg1;
//這是為了找出指示器大概會滑動到那個Tab位置
int pos = Math.round(arg0 + arg1);
for (int i = 0; i < mTabCount; i++) {
TextView textView = (TextView) getChildAt(i);
if (i == pos) {
textView.setTextColor(mSelectedColor);
} else {
textView.setTextColor(mNormalColor);
}
}
//重繪
postInvalidate();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
這裡我們主要在onPageScrolled這個方法裡面去做一下更新的操作;其中
int pos = Math.round(arg0 + arg1);這一行程式碼是為了讓ViewPager切換到一半的是時候會顯示另一個tab被選中,這一點我也是從原始碼裡面看到的,這裡借鑑了一下;最後重新繪製;
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mTabTitleList != null && mTabCount > 0) {
String text = mTabTitleList.get(position);//拿到文字
int textWidth = (int) mPaint.measureText(text);//測量一下寬度
int perWidth = mViewWidth / mTabCount;//每個Tab的平均寬度
rectF.set(
perWidth * (position + ration) + perWidth / 2 - textWidth / 2,
mViewHeight - mPagerIndicatorHeight,
perWidth * (position + ration) + perWidth / 2 + textWidth / 2,
mViewHeight);
canvas.drawRect(rectF, mPaint);
}
}
這裡重寫一下dispatchDraw方法,在super.dispatchDraw方法裡面是繪製子View,等它們都繪製完畢之後,在進行指示器下面的繪製;這一步不要在onDraw方法裡面繪製,因為LinearLayout本身沒有什麼需要繪製的,只是孩子View需要繪製;即使你繪製了也會被dispatchDraw方法繪製的內容覆蓋;
固定標籤的指示器到這裡就就結束了,還有其他一些初始化的工作這裡不說了;下面我們在來看看不固定的情形,也就是標籤的個數比較多的情形;
上面的情形在寫的過程中,大部分程式碼都是參照TabLayout的,先說一下思路吧:
因為這種標籤可以移動
1.首先要把所有的標籤文字全部放到一個容器裡面,寬度按個數均分,容器選HorizontalScrollView,裡面只能有一個View我選擇LinearLayout
2.要關聯上一個ViewPager,並根據ViewPager的改變動態繪製指示器;
先初始化了程式碼:
public ViewPagerIndicator_Scroll(Context context, AttributeSet attrs) {
super(context, attrs);
setHorizontalScrollBarEnabled(false);// Disable the Scroll Bar
initPaint();// 初始化畫筆
/**
* HorizontalScrollView只能包含一個子View
*/
mLinearLayout = new LinearLayout(context);
mLinearLayout.setOrientation(LinearLayout.HORIZONTAL);// 水平方向
mLinearLayout.setLayoutParams(new HorizontalScrollView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
addView(mLinearLayout);//新增
}
這裡要注意是讓setHorizontalScrollBarEnabled讓滾動條不可用,而且要新增一個孩子容器,
我們把這些標籤全部放到這個孩子容器裡面;
生成文字標籤的程式碼:
public TextView generateText(String text) {
TextView textView = new TextView(getContext());
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSize);
textView.setTextColor(mNormalColor);
textView.setGravity(Gravity.CENTER);
textView.setText(text);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
(int) mPaint.measureText(text), //寬度最直接使用畫筆測量出來的寬度
LinearLayout.LayoutParams.MATCH_PARENT);
lp.setMargins(dp2px(5), 0, dp2px(5), 0);//設定了左右間距,可以自己調整
textView.setLayoutParams(lp);
return textView;
}
寬度我沒有使用Wrap_content那樣會有擠壓,這裡直接使用畫筆測量文字的寬度;
我們將生成的表面新增容器裡面之後,我們最後就是和ViewPager的聯動了
public void setViewPager(ViewPager viewPager) {
this.mViewPager = viewPager;
this.mViewPager.addOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int pos) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
int pos = Math.round(position + positionOffset);
for (int i = 0; i < mTabCount; i++) {
TextView textView = (TextView) mLinearLayout.getChildAt(i);
if (i == pos) {
textView.setTextColor(mSelectedColor);
} else {
textView.setTextColor(mNormalColor);
}
}
setIndicatorPositionFromTabPosition(position, positionOffset);
scrollTo(calculateScrollXForTab(position, positionOffset), 0);
}
});
}
在onPageScrolled方法中思路內部是這樣的:
1,設定一下目前將要滑動到那個標籤,我們就並做高亮顯示;
2,根據ViewPager位置資訊改變繪製指示器的下劃線;
3,移動當前View的內容,為了更好的展示
這三個步驟,我全部 都是參照的TabLayout的原始碼;我是搞了半天怎麼也行;最後才想著看看人家的程式碼實現;這也算是我的經驗吧;直接使用了程式碼中的演算法後,出來效果就是內容滑動的和下劃線會對齊;就是這麼神奇!!!貼一下程式碼吧:
public void setIndicatorPositionFromTabPosition(int position, float positionOffset) {
mSelectedPosition = position;
mSelectionOffset = positionOffset;
updateIndicatorPosition();
}
private void updateIndicatorPosition() {
View selectedTitle = mLinearLayout.getChildAt(mSelectedPosition);
int left, right;
if (selectedTitle != null && selectedTitle.getWidth() > 0) {
left = selectedTitle.getLeft();
right = selectedTitle.getRight();
if (mSelectionOffset > 0f && mSelectedPosition < mLinearLayout.getChildCount() - 1) {
// Draw the selection partway between the tabs
View nextTitle = mLinearLayout.getChildAt(mSelectedPosition + 1);
left = (int) (mSelectionOffset * nextTitle.getLeft() +
(1.0f - mSelectionOffset) * left);
right = (int) (mSelectionOffset * nextTitle.getRight() +
(1.0f - mSelectionOffset) * right);
}
} else {
left = right = -1;
}
setIndicatorPosition(left, right);
}
void setIndicatorPosition(int left, int right) {
if (left != mIndicatorLeft || right != mIndicatorRight) {
mIndicatorLeft = left;
mIndicatorRight = right;
ViewCompat.postInvalidateOnAnimation(this);
}
}
private int calculateScrollXForTab(int position, float positionOffset) {
View selectedChild = mLinearLayout.getChildAt(position);
View nextChild = position + 1 < getChildCount()
? getChildAt(position + 1)
: null;
int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0;
int nextWidth = nextChild != null ? nextChild.getWidth() : 0;
// base scroll amount: places center of tab in center of parent
int scrollBase = selectedChild.getLeft() + (selectedWidth / 2) - (getWidth() / 2);
// offset amount: fraction of the distance between centers of tabs
int scrollOffset = (int) ((selectedWidth + nextWidth) * 0.5f * positionOffset);
return (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR)
? scrollBase + scrollOffset
: scrollBase - scrollOffset;
}
相關推薦
自定義View指示器讓指示器下劃線和文字對齊
指示器的標籤分為兩種情形,一種是固定的一般3~5個標籤,另一種的移動的標籤;開發中經常會使用MD風格的TabLayout控制元件,這個控制元件什麼都很好,就是指示器的下滑線和文字不能夠對齊,這一點用過的同學都會知道的,我也在網上找了好久,也找到不到現成的,只好自
Android自定義View完美實現指示器位置隨進度變化的IndicateProgressView
該文章同步釋出在公眾號”LinminTech”上,請在本文最後掃碼關注,獲取更多精彩Android開發文章。 效果圖 需求 在平時開發過程中,UI經常要求實現如上圖所示的ProgressBar,但是Android系統自帶的ProgressBar
自定義View(ListView)下拉重新整理
下拉重新整理的操作流程: 1.使用者手指在ListView頁面按下並下拉 2.出現一個提示View在listView頂部 3.listView內容更新,頂部view顯示後隱藏 具體實現步驟 建立繼承Listview的RefreshListView,並新增頂部提示vi
Android 自定義view(1) --- Attr、Style和Theme詳解
轉載:https://www.jianshu.com/p/dd79220b47dd 概念說明: Attr:屬性,風格樣式的最小單元; Style:風格,它是一系列Attr的集合用以定義一個View
自定義view,繪製階段進度progressBar,階段與圖片和文字對齊
沒用seekbar或者progressbar原生控制元件,通過繪製實現。只講下有用的思想,無關屬性不解釋,也不用看。 主要看onDraw方法程式碼: 繪製背景線,canvas.drawRect線繪製了第一條線,因為需要漸變,可以看到canvas.drawPath是從第
自定義控制元件讓TextView的drawableLeft與文字一起居中顯示
Drawable[] drawables = getCompoundDrawables(); if (drawables !=
自定義view,可拖拽進度和吸附效果的圓形進度條
前言 最近接到一個需求,第一眼看到ui互動效果時,瞬間想對產品小哥說“尼瑪,這麼會玩,你咋不上天”。確認了具體互動細節,喝了兩口農夫三拳,開始了兩耳不聞窗外事,一心只想擼程式碼的過程。 先上ui效果 說明: 外圈弧形上面是進度的標記點,預設在12點位置,也是
自定義控制元件讓EditText的drawableLeft與文字一起居中顯示
如上圖 專案中要求的顯示效果是serch圖片和EditText的文字一起在中間位置。 嘗試設定了很多EditText均不能實現滿意的效果,其中: android:drawableLeft="" android:drawableRight=""設定的位置均在兩邊,不能滿足
btn下劃線 字型左對齊
NSString *appleNum = [NSString stringWithFormat:@"%@",ObjectToNumber(model.amount)]; // 下劃線 NSDictiona
DIV+CSS 讓同一行的圖片和文字對齊【轉】
在div+css佈局中,如果一行(或一個DIV)內容中有圖片和文字的話,圖片和文字往往會一個在上一個在下,這是一個新手都會遇到問題,我的解決方法有三: 1.新增CSS屬性:vertical-align:middle; 程式碼: <style> a img{b
CSS 讓同一行的圖片和文字對齊
大家在做前端開發的時候那,經常會遇到img標籤和文字在同一行。 那麼我剛開始的時候那是利用的float浮動佈局解決的,定位佈局(相容性需要調整 不划算)下面給大家介紹一些其他的方法: 1.新增CSS屬性:vertical-align:middle; 程式碼: &l
仿新浪微博首頁具有泥鰍蚯蚓滑動效果的可自定義View的Android ViewPager指示器
說到蚯蚓滑動效果的指示器,市面上很常見 也許是普通的控制元件習以為常了,刷微博的時候看到微博首頁的設計覺得很有意思 於是在公司的蚯蚓滑動指示器的基礎上進行了加工,寫了這麼一個指示器,效果如下所示 程式碼不多,正當我竊喜的時候我突然發現遇到個坑,點選popupwindo
Android開發實戰2----圓點導航指示器(使用自定義View實現)
一、專案概述 當我們使用viewpager進行圖片預覽時,底部一般情況都會出現圓點導航指示器,效果如圖所示: 二、不好的一種處理方式: 剛開始的情況下,我們會現在activity中先定義一個LinearLayout,然後對於這個LinearLayout進行增刪Imag
安卓自定義View——網易顏色漸變效果指示器
一直想寫部落格來著,可惜直到現在才真正抽出時間。最近一直在研究網易新聞這個UI框架,發現了一些很值得借鑑的效果,當然,網上也不乏這方面的介紹。本文主要實現的指示器效果為字型顏色和大小漸變,廢話不多說獻上效果圖: 實現效果主要包括: 指示器背景可以根據使用者自己定製形
模仿微博ViewPage指示器滑動效果的自定義View
效果圖 偶然看到在微博的最新版本看到發現介面和個人介面的幾個Tab滑動的指示器添加了動畫,看起來比以前的線條滑動看起來生動些。 實現 實現滑動是我們Android開發經常運用到的ViewPage 線條的滑動則是根據ViewPage
Android從零開搞系列:自定義View(4)基本的自定義ViewPager指示器+開源專案分析(上)
基本的自定義ViewPager的指示器 當然關於ViewPager指示器,如果只需要簡潔大方,那麼我們最簡單的方案就是使用TabLayout+ViewPager。 當然咱們也有很多非常不錯的開源框架可以選擇。 本次的記錄的內容就是
Banner+自定義View+SmartRefreshLayout下拉重新整理上拉載入更多
仿美團開源專案整體架構和首頁其實早就完成了,前段時間家裡各種事情搞得心力交瘁,停更了一段時間。甚至一度動搖繼續這個專案的決心,因為最近在學前端,在技術的深度和廣度之間一直糾結搖擺不定。一個聲音是繼續完成這個專案,把安卓玩的更深入一些;另一個聲音是趕緊學前端吧
自定義View-剪下區
Canvas 提供了剪下區的功能,剪下區可以是一個 Rect 或者是一個 Path,兩個剪下區還能進 行圖形運算,得到更加複雜的剪下區。我們來看看相關的方法: public boolean clipRect(Rect rect) public boolean clipRect(Rect
自定義View——仿騰訊TIM下拉重新整理View
一 概述 自定義 View 是 Android 開發裡面的一個大學問。偶然間看到 TIM 郵箱介面的重新整理 View 還挺好玩的,於是就自己動手實現了一個,先看看 TIM 裡邊的效果圖: 二 需求分析 看到上面的動圖,大概也知道我們需要實現的功能: 根據拖動的進度來移動小球的位
Android自定義View之(下拉重新整理+側滑刪除)
以前專案中用到了一個放qq的側滑刪除的效果,結果github上一搜就copy了一個,不得不說大神們寫的真心牛逼,那個時候呢看到一個東西能用就可以了,也不管怎麼實現的,現在反過來一看,原來自定義還可以這麼玩,當然,前面專案中也因此出現了一個bug,就是我使用的是P