上一篇我們簡單實(shí)踐了下自定義屬性部分Android-自定義View-自定義屬性,現(xiàn)在我們看看第三方的自定義控件源碼,混個(gè)眼熟先。
Like,youth5201314/banner,下載一下zip包,然后解壓,AS導(dǎo)入module即可:

1. 從我們經(jīng)常使用的類(Banner)+配置入手(app:)
<com.youth.banner.Banner
android:id="@+id/finba_banner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
app:banner_default_image="@drawable/home_list_img_default"
app:banner_layout="@layout/banner_me"
app:image_scale_type="center_crop"
app:indicator_drawable_selected="@drawable/banner_rectangle_white_radius"
app:indicator_drawable_unselected="@drawable/banner_rectangle_gray_radius"
app:indicator_height="4dp"
app:indicator_margin="3dp"
app:indicator_width="10dp"
app:title_textsize="16sp" />

2. 看構(gòu)造函數(shù) - 是不是會(huì)有之前相識(shí)的感覺妮?
public Banner(Context context) {
this(context, null);
}
public Banner(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Banner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
titles = new ArrayList<>();
imageUrls = new ArrayList<>();
imageViews = new ArrayList<>();
indicatorImages = new ArrayList<>();
dm = context.getResources().getDisplayMetrics();
indicatorSize = dm.widthPixels / 80;
initView(context, attrs);
}
這個(gè)地方?jīng)]有第四個(gè)構(gòu)造函數(shù),因?yàn)榈谒膫€(gè)是5.0以后新增的,所以這個(gè)暫時(shí)不需要也沒關(guān)系。你看上面的寫法,是不是和之前我們了解的一樣。或許我們今后開始自定義做自己的自定義控件就是這樣的方式吧!
3. 重點(diǎn)看下自定義View的部分的具體操作 - handleTypedArray(...)

private void handleTypedArray(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Banner);
mIndicatorWidth = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_width, indicatorSize);
mIndicatorHeight = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_height, indicatorSize);
mIndicatorMargin = typedArray.getDimensionPixelSize(R.styleable.Banner_indicator_margin, BannerConfig.PADDING_SIZE);
mIndicatorSelectedResId = typedArray.getResourceId(R.styleable.Banner_indicator_drawable_selected, R.drawable.gray_radius);
mIndicatorUnselectedResId = typedArray.getResourceId(R.styleable.Banner_indicator_drawable_unselected, R.drawable.white_radius);
scaleType = typedArray.getInt(R.styleable.Banner_image_scale_type, scaleType);
delayTime = typedArray.getInt(R.styleable.Banner_delay_time, BannerConfig.TIME);
scrollTime = typedArray.getInt(R.styleable.Banner_scroll_time, BannerConfig.DURATION);
isAutoPlay = typedArray.getBoolean(R.styleable.Banner_is_auto_play, BannerConfig.IS_AUTO_PLAY);
titleBackground = typedArray.getColor(R.styleable.Banner_title_background, BannerConfig.TITLE_BACKGROUND);
titleHeight = typedArray.getDimensionPixelSize(R.styleable.Banner_title_height, BannerConfig.TITLE_HEIGHT);
titleTextColor = typedArray.getColor(R.styleable.Banner_title_textcolor, BannerConfig.TITLE_TEXT_COLOR);
titleTextSize = typedArray.getDimensionPixelSize(R.styleable.Banner_title_textsize, BannerConfig.TITLE_TEXT_SIZE);
mLayoutResId = typedArray.getResourceId(R.styleable.Banner_banner_layout, mLayoutResId);
bannerBackgroundImage = typedArray.getResourceId(R.styleable.Banner_banner_default_image, R.drawable.no_banner);
typedArray.recycle();
}
其實(shí)就是獲取我們使用banner進(jìn)行配置時(shí)的一些個(gè)屬性。同時(shí)如果你不設(shè)置也是有默認(rèn)值的。獲取如下配置喲....TypedArray,不就是我們之前了解過的獲取方式么。

其中有一個(gè)屬性app:banner_layout我特別提一下(因?yàn)楹芏鄷r(shí)候可以自定義banner的樣式滿足產(chǎn)品需求,所以我們有必要關(guān)注;同時(shí)banner的一些個(gè)源碼我也自定義過一些實(shí)現(xiàn),所以我覺得加深對(duì)框架的理解還是蠻重要的)

3.1 然后你可以看看它如下的布局呀,屬性配置文件呀,基本也就熟套了...

**3.2 **其他的文件也可以過過眼,有時(shí)間一定要研究一下,因?yàn)槲募痛a不多,所以相對(duì)來講還好。而且你如果利用Viewpaper實(shí)現(xiàn)過banner無限輪播控件的話,看起這個(gè)應(yīng)該不會(huì)那么的難了。

主要是幾個(gè)部分:Banner(具體的控件實(shí)現(xiàn),對(duì)外提供) + 滑動(dòng)動(dòng)畫(transformer) + 圖片加載器....其中BannerScroller部分用到了ViewPaper的一個(gè)方法mScroller,這個(gè)方法是通過反射獲取 - 所以關(guān)于反射的知識(shí)有必要學(xué)習(xí),我前面有文章了解過Android-自定義注解-反射基礎(chǔ):
private void initViewPagerScroll() {
try {
Field mField = ViewPager.class.getDeclaredField("mScroller");
mField.setAccessible(true);
mScroller = new BannerScroller(viewPager.getContext());
mScroller.setDuration(scrollTime);
mField.set(viewPager, mScroller);
} catch (Exception e) {
Log.e(tag, e.getMessage());
}
}
4. 最后關(guān)于我改動(dòng)的地方,提一下,就是一個(gè)文本顯示關(guān)鍵字高亮背景的效果

主要就是利用SpannableString進(jìn)行背景顏色文本顯示。
先看高亮工具類 HighLightKeyWordUtil.java
package com.youth.banner;
import android.content.Context;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HighLightKeyWordUtil {
/**
* @param color 關(guān)鍵字顏色
* @param text 文本
* @param keyword 關(guān)鍵字
* @return
*/
public static SpannableString getHighLightKeyWord(int color, String text, String keyword) {
SpannableString s = new SpannableString(text);
Pattern p = Pattern.compile(keyword);
Matcher m = p.matcher(s);
while (m.find()) {
int start = m.start();
int end = m.end();
s.setSpan(new ForegroundColorSpan(color), start, end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return s;
}
/**
* @param color 關(guān)鍵字顏色
* @param text 文本
* @param keyword 多個(gè)關(guān)鍵字
* @return
*/
public static SpannableString getHighLightKeyWord(int color, String text,String[] keyword) {
SpannableString s = new SpannableString(text);
for (int i = 0; i < keyword.length; i++) {
Pattern p = Pattern.compile(keyword[i]);
Matcher m = p.matcher(s);
while (m.find()) {
int start = m.start();
int end = m.end();
s.setSpan(new ForegroundColorSpan(color), start, end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return s;
}
/**
* @param color 關(guān)鍵字背景顏色
* @param text 文本
* @param keyword 關(guān)鍵字
* @return
*/
public static SpannableString getBackgroudKeyWord(int tvcolor, int color, String text, String keyword) {
SpannableString s = new SpannableString(text);
Pattern p = Pattern.compile(keyword);
Matcher m = p.matcher(s);
//while (m.find()) {
if (m.find()) {
int start = m.start();
int end = m.end();
s.setSpan(new RoundBackgroundColorSpan(color, tvcolor, 10), start, end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return s;
}
/**
* @param color 關(guān)鍵字背景顏色
* @param text 文本
* @param keywords 多個(gè)關(guān)鍵字
* @return
*/
public static SpannableString getBackgroudKeyWord(int[] tvcolor, int[] color, String text, String[] keywords) {
SpannableString s = new SpannableString(text);
int strLength = 0;
for (int i = 0; i < keywords.length; i++) {
///< 必須是開頭的才標(biāo)記背景,所以索引必須小于開頭內(nèi)容長度
strLength += keywords[i].length();
Pattern p = Pattern.compile(keywords[i]);
Matcher m = p.matcher(s);
///< 只找開頭的,標(biāo)題中的不找
//while (m.find()) {
//if (m.find()) {
if (m.find() && m.start() < strLength) {
int start = m.start();
int end = m.end();
s.setSpan(new RoundBackgroundColorSpan(color[i], tvcolor[i], 10), start, end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return s;
}
}
**然后使用的地方Banner.java --- **//bannerTitle.setText(titles.get(position - 1));是之前的寫法
@Override
public void onPageSelected(int position) {
......
case BannerConfig.CIRCLE_INDICATOR_TITLE:
bannerTitle.setText(HighLightKeyWordUtil.getBackgroudKeyWord(
new int[]{Color.parseColor("#ffffff"), Color.parseColor("#ffffff")},
new int[]{Color.parseColor("#febc48"), Color.parseColor("#f13b2f")},
titles.get(position - 1), new String[]{"獨(dú)家", "首發(fā)"}));
//bannerTitle.setText(titles.get(position - 1));
break;
......
}
Last,基本的自定義屬性和獲取我們大概來講下。這個(gè)框架相對(duì)還好,不是特別復(fù)雜。有些框架就復(fù)雜了,不僅僅是這點(diǎn)代碼了。而且可能還融合了其他的優(yōu)秀的第三方,所以看起來相對(duì)更難一些。 不過我們慢慢來嘛。順便也已多看看別人做框架是怎么做的,怎么別人如此優(yōu)秀,而自己菜的像渣渣妮...
不是心靈雞湯 -- 輕輕的我將離開你,請(qǐng)將眼角的淚拭去.....嘿嘿!