/**
* 成员信息列表 -右侧的导航条
*/
class EnglishIndexBar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {

private var mIndex = -1
private var mTextSize: Int = 0
private var mSelectTextColor: Int = 0
private var mSelectTextSize: Int = 0
private var mHintTextColor: Int = 0
private var mHintTextSize: Int = 0
private var mHintCircleRadius: Int = 0
private var mWaveRadius: Int = 0
private var mContentPadding: Int = 0
private var mBarWidth: Int = 0
private var mIndexWord : String? = "A"
private var isUpdateView : Boolean = false
private var mSlideBarRect: RectF= RectF()
private lateinit var mTextPaint: TextPaint
private lateinit var mPaint: Paint
private lateinit var mWavePaint: Paint
private lateinit var mHintPaint: Paint
private var mSelect: Int = 0
private var mPreSelect: Int = 0
private var mNewSelect: Int = 0
private lateinit var mTtextBgPaint: Paint
private var mRatioAnimator: ValueAnimator? = null
private var mAnimationRatio: Float = 0.toFloat()
private var mListener: OnLetterChangeListener? = null
private var mTouchY: Int = 0
private lateinit var mBitmap: Bitmap
private var mIsActionDown: Boolean = false
private var mIsShowWave: Boolean = true
private var mViewPager: NoScrollViewPager? = null
private var mLetters = arrayOf("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
"L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z") //设置字母索引数据

init {
initAttribute(attrs, defStyleAttr)
initData()
}

private fun initAttribute(attrs: AttributeSet?, defStyleAttr: Int) {
val typeArray = context.obtainStyledAttributes(attrs, R.styleable.EnglishIndexBarTwo, defStyleAttr, 0)
mTextSize = typeArray.getDimensionPixelOffset(R.styleable.EnglishIndexBarTwo_textSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 9f,
resources.displayMetrics).toInt())
mSelectTextColor = typeArray.getColor(R.styleable.EnglishIndexBarTwo_selectTextColor, Color.parseColor("#FFFFFF"))
mSelectTextSize = typeArray.getDimensionPixelOffset(R.styleable.EnglishIndexBarTwo_selectTextSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10f,
resources.displayMetrics).toInt())
mHintTextColor = typeArray.getColor(R.styleable.EnglishIndexBarTwo_hintTextColor, Color.parseColor("#FFFFFF"))
mHintTextSize = typeArray.getDimensionPixelOffset(R.styleable.EnglishIndexBarTwo_hintTextSize,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16f,
resources.displayMetrics).toInt())
mHintCircleRadius = typeArray.getDimensionPixelOffset(R.styleable.EnglishIndexBarTwo_hintCircleRadius,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24f,
resources.displayMetrics).toInt())
mWaveRadius = 20
mContentPadding = 2
mBarWidth = typeArray.getDimensionPixelOffset(R.styleable.EnglishIndexBarTwo_barWidth,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0f,
resources.displayMetrics).toInt())
if (mBarWidth == 0) {
mBarWidth = 2 * mTextSize
}
typeArray.recycle()
}

@TargetApi(Build.VERSION_CODES.KITKAT)
private fun initData() {
mTextPaint = TextPaint()
mPaint = Paint()
mTextPaint.isAntiAlias = true
mPaint.isAntiAlias = true
mTtextBgPaint = Paint()
mTtextBgPaint.isAntiAlias = true
mWavePaint = Paint()
mWavePaint.isAntiAlias = true
mHintPaint = Paint()
mSelect = -1
mBitmap = BitmapFactory.decodeResource(resources, R.drawable.bg_index_water)

}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val contentLeft = (measuredWidth - mBarWidth ).toFloat()
val contentRight = (measuredWidth ).toFloat()
//val contentTop = mBarPadding.toFloat()
val contentBottom = (measuredHeight).toFloat()
mSlideBarRect.set(contentLeft, 0f, contentRight, contentBottom)

}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//绘制slide bar 上字母列表
drawLetters(canvas)
}

/**
* 绘制slide bar 上字母列表
*/
private fun drawLetters(canvas: Canvas) {
//绘制圆角矩形
mPaint.style = Paint.Style.FILL
mPaint.color = Color.parseColor("#00000000")
canvas.drawRoundRect(mSlideBarRect, mBarWidth / 2.0f, mBarWidth / 2.0f, mPaint)
//绘制描边
canvas.drawRoundRect(mSlideBarRect, mBarWidth / 2.0f, mBarWidth / 2.0f, mPaint)
//顺序绘制文字
val itemHeight = (mSlideBarRect.bottom - mSlideBarRect?.top - (mContentPadding * 2).toFloat()) / mLetters.size
for (index in mLetters.indices) {
val baseLine = getTextBaseLineByCenter(
mSlideBarRect.top + mContentPadding.toFloat() + itemHeight * index + itemHeight / 2, mTextPaint, mTextSize)
mTextPaint.textSize = mTextSize.toFloat()
mTextPaint.textAlign = Paint.Align.CENTER
val pointX = mSlideBarRect.left + (mSlideBarRect.right - mSlideBarRect.left) / 2.0f

if(mLetters.contains(mIndexWord) && isUpdateView && mIndex == index){ //标识列表已经匹配的字母
isUpdateView = false
mTtextBgPaint.color = Color.parseColor("#61BE82")
mTextPaint.color = Color.parseColor("#FFFFFF")
if(mSelect != -1 && mSelect < mLetters.size){ // 绘制提示字符
if(mIsShowWave) {
canvas.drawBitmap(mBitmap, pointX - SystemUtil.sp2px(context, 48),
baseLine - SystemUtil.sp2px(context, 15), mWavePaint)
}
if (mSelect != -1) {
mHintPaint.color = mHintTextColor
mHintPaint.textSize = SystemUtil.sp2px(context,15).toFloat()
mHintPaint.textAlign = Paint.Align.CENTER
canvas.drawText(mLetters[index], pointX-SystemUtil.sp2px(context,35), baseLine+SystemUtil.sp2px(context,3), mHintPaint)
}
}
}else{
mTtextBgPaint.color = Color.parseColor("#00000000")
mTextPaint.color = Color.parseColor("#555555")
}
canvas.drawCircle( pointX, baseLine-dp2px(3),SystemUtil.sp2px(context,7).toFloat(),mTtextBgPaint)
canvas.drawText(mLetters[index], pointX, baseLine, mTextPaint)

}
}
/**
* dp转px
* @param dpValue
* @return
*/
fun dp2px(dpValue: Int): Int {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue.toFloat(), resources.displayMetrics).toInt()
}
/**
* 通过获取到的字母来匹配字母检索列表的字母背景
* @parem Words
*/
fun notifiChangeIndexWordBg(word : String?){
if(mLetters.contains(word)){
this.mIndexWord = word
this.mIndex = mLetters.indexOf(word)
invalidate() //刷新界面,只会调用onDraw()方法
isUpdateView = true
}
}

/**
* 给定文字的center获取文字的base line
*/
private fun getTextBaseLineByCenter(center: Float, paint: TextPaint, size: Int): Float {
paint.textSize = size.toFloat()
val fontMetrics = paint.fontMetrics
val height = fontMetrics.bottom - fontMetrics.top
return center + height / 2 - fontMetrics.bottom
}

override fun dispatchTouchEvent(event: MotionEvent): Boolean {
val y = event.y
val x = event.x
mPreSelect = mSelect
mNewSelect = (y / (mSlideBarRect.bottom - mSlideBarRect.top) * mLetters.size).toInt()
when (event.action) {
MotionEvent.ACTION_DOWN -> {
//保证down的时候在bar区域才相应事件
mViewPager?.setNoScroll(true)
if (x < mSlideBarRect.left || y < mSlideBarRect.top || y > mSlideBarRect.bottom) {
mIsActionDown = true
mIsShowWave = true
if(mNewSelect != -1 && mNewSelect < mLetters.size) {
mSelect = mNewSelect
if (mListener != null) {
mListener?.onLetterChange(mLetters[mNewSelect])
}
notifiChangeIndexWordBg(mLetters[mNewSelect])
}
val circleCenterX = measuredWidth + mHintCircleRadius - (2.0f * mWaveRadius
+ 2.0f * mHintCircleRadius) * mAnimationRatio
val canvas = Canvas()
canvas.drawBitmap(mBitmap, circleCenterX - SystemUtil.dp2px(context, 5),
mTouchY.toFloat() - SystemUtil.dp2px(context, 3), mWavePaint)
mViewPager?.postDelayed({
isUpdateView = true
mIsShowWave = false
invalidate()
},1000)
return false
}
mTouchY = y.toInt()
startAnimator(1.0f)
}
MotionEvent.ACTION_MOVE -> {
mIsActionDown = false
mIsShowWave = true
mTouchY = y.toInt()
if (mPreSelect != mNewSelect && mNewSelect >= 0 && mNewSelect < mLetters.size) {
mSelect = mNewSelect
if (mListener != null) {
mListener?.onLetterChange(mLetters[mNewSelect])
}
if(mSelect != -1) {
notifiChangeIndexWordBg(mLetters[mSelect])
}
}
}
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
startAnimator(0f)
mSelect = -1
mViewPager?.setNoScroll(false)
}
else -> {
}
}
return true
}

/**
* get activity's ViewPager
*/
fun getActivitysViewpager(viewPager: NoScrollViewPager){
mViewPager = viewPager
}
@SuppressLint("NewApi")
private fun startAnimator(value: Float) {
if (mRatioAnimator == null) {
mRatioAnimator = ValueAnimator()
}
mRatioAnimator?.cancel()
mRatioAnimator?.setFloatValues(value)
mRatioAnimator?.addUpdateListener { value ->
mAnimationRatio = value.animatedValue as Float
//球弹到位的时候,并且点击的位置变了,即点击的时候显示当前选择位置
if (mAnimationRatio == 1f && mPreSelect != mNewSelect) {
if (mNewSelect >= 0 && mNewSelect < mLetters.size) {
mSelect = mNewSelect
if (mListener != null) {
mListener?.onLetterChange(mLetters[mNewSelect])
}
if(mSelect != -1) {
notifiChangeIndexWordBg(mLetters[mSelect])
}
}
}
invalidate()
}
mRatioAnimator?.start()
}

fun setOnLetterChangeListener(listener: OnLetterChangeListener) {
this.mListener = listener
}

interface OnLetterChangeListener {
fun onLetterChange(letter: String)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
这里需要注意的就是Adapter控制器,它需要把数据和第一个字的首字母关联起来,通过创建一个数据实体类:

/**
* 悬浮窗实体
* @author guotianhui
*/
public class TitleEntity{

private AllMemberData mValue;
private String mSortLetters;

public AllMemberData getValue() {
return mValue;
}

public void setValue(AllMemberData value) {
mValue = value;
}

public String getSortLetters() {
return mSortLetters;
}

public void setSortLetters(String sortLetters) {
mSortLetters = sortLetters;
}

@Override
public String toString() {
return "TitleEntity{" +
"mValue=" + mValue +
", mSortLetters='" + mSortLetters + '\'' +
'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
当从网络获取到数据之后,我们就可以通过获取到数据之后,就可以把数据转换成我们需要的格式:

for (titleItem in result.data!!) {
val titleEntity = TitleEntity()
titleEntity.value = titleItem
titleEntity.sortLetters = getEnglishIndexFristWord(titleItem)
mMemberInfotList.add(titleEntity)
}
1
2
3
4
5
6
其中RecyclerView悬浮的指示条是通过自定义分割线的形式自定义出来的:

mRecyclerView.addItemDecoration (TitleItemDecoration(context))
1
指示条的自定义代码如下:

/**
* 所有成员列表头部的悬浮title
* @author guotianhui
*/
public class TitleItemDecoration extends RecyclerView.ItemDecoration {

private int mItemHeight;
private int mTextPadding;
private int mTextSize;
private int mTextColor;
private int mBackgroundColor;
private TextPaint mTitleTextPaint;
private Paint mBackgroundPaint;

public TitleItemDecoration(Context context) {
mItemHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 23, context.getResources().getDisplayMetrics());
mTextPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 21, context.getResources().getDisplayMetrics());
mTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, context.getResources().getDisplayMetrics());
mTextColor = Color.parseColor("#666666");
mBackgroundColor = Color.parseColor("#f4f4f4");
mTitleTextPaint = new TextPaint();
mTitleTextPaint.setAntiAlias(true);
mTitleTextPaint.setTextSize(mTextSize);
mTitleTextPaint.setColor(mTextColor);
mBackgroundPaint = new Paint();
mBackgroundPaint.setAntiAlias(true);
mBackgroundPaint.setColor(mBackgroundColor);
}

/**
* 绘制标题
*/
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if (parent.getAdapter() == null || !(parent.getAdapter() instanceof AllMemberDataAdapter)) {
return;
}
AllMemberDataAdapter adapter = (AllMemberDataAdapter) parent.getAdapter();
if (adapter.getMItemFirstWordList() == null || adapter.getMItemFirstWordList().isEmpty()) {
return;
}
for (int i = 0; i < parent.getChildCount(); i++) {
final View child = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(child);
if (titleAttachView(child, parent)) {
drawTitleItem(c, parent, child, adapter.getSortLetters(position));
}
}
}

/**
* 绘制悬浮标题
*/
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
if (parent.getAdapter() == null || !(parent.getAdapter() instanceof AllMemberDataAdapter)) {
return;
}
AllMemberDataAdapter adapter = (AllMemberDataAdapter) parent.getAdapter();
if (adapter.getMItemFirstWordList() == null || adapter.getMItemFirstWordList().isEmpty()) {
return;
}
View firstView = parent.getChildAt(0);
int firstAdapterPosition = parent.getChildAdapterPosition(firstView);
c.save();
//找到下一个标题对应的adapter position
int nextLetterAdapterPosition = adapter.getNextSortLetterPosition(firstAdapterPosition);
if (nextLetterAdapterPosition != -1) {
//下一个标题view index
int nextLettersViewIndex = nextLetterAdapterPosition - firstAdapterPosition;
if (nextLettersViewIndex < parent.getChildCount()) {
View nextLettersView = parent.getChildAt(nextLettersViewIndex);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) nextLettersView.getLayoutParams();
int nextToTop = nextLettersView.getTop() - params.bottomMargin - parent.getPaddingTop();
if (nextToTop < mItemHeight * 2) {
//有重叠
c.translate(0, nextToTop - mItemHeight * 2);
}
}
}
mBackgroundPaint.setColor(mBackgroundColor);
c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(),
parent.getPaddingTop() + mItemHeight, mBackgroundPaint);
mTitleTextPaint.setTextSize(mTextSize);
mTitleTextPaint.setColor(mTextColor);
c.drawText(adapter.getSortLetters(firstAdapterPosition),
parent.getPaddingLeft() + firstView.getPaddingLeft() + mTextPadding,
getTextBaseLineByCenter(parent.getPaddingTop() + mItemHeight / 2, mTitleTextPaint),
mTitleTextPaint);
c.restore();
}

/**
* 设置空出绘制标题的区域
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (titleAttachView(view, parent)) {
outRect.set(0, mItemHeight, 0, 0);
} else {
super.getItemOffsets(outRect, view, parent, state);
}
}

/**
* 绘制标题信息
*/
private void drawTitleItem(Canvas c, RecyclerView parent, View child, String letters) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
//绘制背景
c.drawRect(parent.getPaddingLeft(), child.getTop() - params.bottomMargin - mItemHeight,
parent.getWidth() - parent.getPaddingRight(), child.getTop() - params.bottomMargin, mBackgroundPaint);

float textCenterY = child.getTop() - params.bottomMargin - mItemHeight / 2;
//绘制标题文字
c.drawText(letters, parent.getPaddingLeft() + child.getPaddingLeft() + mTextPadding,
getTextBaseLineByCenter(textCenterY, mTitleTextPaint), mTitleTextPaint);
}
public float getTextBaseLineByCenter(float center, TextPaint paint) {
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float height = fontMetrics.bottom - fontMetrics.top;
return center + height / 2 - fontMetrics.bottom;
}
/**
* 判断指定view的上方是否要空出绘制标题的位置
*
* @param view  指定的view
* @param parent 父view
*/
private boolean titleAttachView(View view, RecyclerView parent) {
if (parent.getAdapter() == null || !(parent.getAdapter() instanceof AllMemberDataAdapter)) {
return false;
}
AllMemberDataAdapter adapter = (AllMemberDataAdapter) parent.getAdapter();
if (adapter.getMItemFirstWordList() == null || adapter.getMItemFirstWordList().isEmpty()) {
return false;
}
int position = parent.getChildAdapterPosition(view);
//第一个一定要空出区域 + 每个都和前面一个去做判断,不等于前一个则要空出区域
return position == 0 ||
null != adapter.getMItemFirstWordList().get(position) && !adapter.getSortLetters(position).equals(adapter.getSortLetters(position -1));

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
下面就是Adapter的代码:

/**
*所有成员列表的Adapter
* @author guotianhui
*/
class AllMemberDataAdapter : BaseQuickAdapter<TitleEntity, BaseViewHolder> {

var mItemFirstWordList: ArrayList<TitleEntity>

constructor(layoutResId: Int, dataList: ArrayList<TitleEntity>) : super(layoutResId, dataList) {
mItemFirstWordList = dataList
}

fun getSortLetters(position: Int): String? {
return mItemFirstWordList[position].sortLetters
}

fun getSortLettersFirstPosition(letters: String): Int {
if (mItemFirstWordList == null || mItemFirstWordList.isEmpty()) {
return -1
}
var position = -1
for (index in mItemFirstWordList.indices) {
if (mItemFirstWordList[index].sortLetters == letters) {
position = index
break
}
}
return position
}

fun getNextSortLetterPosition(position: Int): Int {
if (mItemFirstWordList == null || mItemFirstWordList.isEmpty() || mItemFirstWordList.size <= position + 1) {
return -1
}
var resultPosition = -1
for (index in position + 1 until mItemFirstWordList.size) {
if (mItemFirstWordList[position]!= mItemFirstWordList[index]) {
resultPosition = index
break
}
}
return resultPosition
}

override fun getItemCount(): Int {
return if (mItemFirstWordList == null) 0 else mItemFirstWordList.size
}

override fun convert(helper: BaseViewHolder?, titleItem: TitleEntity?) {
val item = titleItem?.value
val headerImageView = helper?.getView<AppCompatImageView>(R.id.iv_member_header)

if(ObjectUtils.isNotEmpty(item?.studentHeadPic)) {
ImageLoader.newInstance().loadImageHeader(headerImageView, item?.studentHeadPic, R.drawable.icon_header_default)
}else{
headerImageView?.setImageResource(R.drawable.icon_header_default)
}
if(ObjectUtils.isNotEmpty(item?.nickname)) {
helper?.setText(R.id.tv_member_name, item?.nickname)
}else{
helper?.setText(R.id.tv_member_name, item?.studentName)
}
if(item?.studentGender ==1){ //1是男 2是女,0不要展示
helper?.setImageResource(R.id.iv_member_sex, R.drawable.ic_male_member)
}else{
helper?.setImageResource(R.id.iv_member_sex, R.drawable.ic_member_female)
}
if(item?.studentVIPFlag == true){
helper?.setVisible(R.id.iv_member_coach_state,true)
}else{
helper?.setVisible(R.id.iv_member_coach_state,false)
}
if(item?.studentLevel!! >=0) { //兼容小于0的情况
helper?.setText(R.id.tv_user_level, "Lv. " + item?.studentLevel.toString())
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
以上就是这个控件功能的所有代码实现。下面的是我个人的自定义控件学习心得:安卓自定义控件,除了自定义一个自定义一个ViewGroup以外,我们还可以自定义一个View,虽然都是自定义。却又有很大的不同。因为自定义ViewGroup可以理解为自定义了一个布局容器,这个布局容器的控件其实是可以自己通过Xml布局文件来定义的。但是如果你是自定义View,也就是说你直接继承自View。因为View本身就是一个父类。而且View不可以像ViewGroup一样添加一个布局。这样我们就只能通过实现View的onMeasure()、onDraw()、onLayout()方法。这个三个方法,通过字面意思我们可以知道它们的调用顺序,先是需要测量获取自定义控件的框高,然后在调用onDarw绘制。
---------------------

转载于:https://www.cnblogs.com/ly570/p/10970832.html

安卓自定义边栏英文索引控件相关推荐

  1. mysql抽屉图标_React Native自定义组件实现抽屉菜单控件效果

    一.需求分析 原生开发中,自定义View可谓是屡见不鲜的事情,往往系统的控件总不能满足现实的需求.五花八门的产品设计需要我们做出不同的View.关于自定义View的内容网上已经有很多的博文,本篇博客要 ...

  2. WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探

    原文:WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探 最近因为项目需要,开始学习如何使用WPF开发桌面程序.使用WPF一段时间之后,感觉WPF的开发思 ...

  3. android lrc 歌词view,自定义View强势来袭,用自定义View实现歌词显示控件下篇之自定义LyricView的实现...

    在上篇中,我与大家分享了关于如何进行*.lrc歌词文件的解析,以及将解析完成后的歌词展示在镶嵌在ScrollView中的TextView上,就这样而言,一个简单的歌词显示功能也就实现了. 但是,如何才 ...

  4. Kotlin 自定义View之实现标尺控件(选择身高、体重等)

    本篇文章讲的是Kotlin 自定义view之实现标尺控件Ruler,以选择身高.体重等.开发中,当我们需要获取用户的身高和体重等信息时,如果直接让他们输入,显然体验不够好.像类似于唯品会.好轻等APP ...

  5. 安卓学习笔记21:常用控件 - 列表视图

    文章目录 零.学习目标 一.列表视图概述 (一)继承关系图 (二)列表视图四要素 (三)四种适配器 二.基于数组适配器使用列表视图案例 -- 阅读古诗 (一)数组适配器 (二)运行效果 (三)涉及知识 ...

  6. 安卓学习笔记16:常用控件 - 标签

    文章目录 零.学习目标 一.继承关系图 二.标签常用属性 三.教学案例 - 标签演示 (一)运行效果 (二)涉及知识点 (三)实现步骤 1.创建安卓应用[TextViewDemo] 2.布局资源文件a ...

  7. Android 自定义View修炼-打造完美的自定义侧滑菜单/侧滑View控件(转)

    一.概述 在App中,经常会出现侧滑菜单,侧滑滑出View等效果,虽然说Android有很多第三方开源库,但是实际上 咱们可以自己也写一个自定义的侧滑View控件,其实不难,主要涉及到以下几个要点: ...

  8. android自定义组件属性,Android组合控件详解 自定义属性

    组合控件详解 & 自定义属性 组合控件是自定义控件的一种,只不过它是由其他几个原生控件组合而成,故名组合控件. 在实际项目中,GUI 会遇到一些可以提取出来做成自定义控件情况. 一个自定义控件 ...

  9. android 顶部导航栏 自定义,Android自定义NavigationController - 安卓自定义导航栏 --【WJ】...

    注意: 本文主要介绍安卓自定义顶部导航栏(iOS中成为NavigationBar):写的不尽如人意的地方,请见谅~ 概述如下: 环境 :Android Studio 1.4 for Mac 语言 :如 ...

最新文章

  1. python 把元组转为列表
  2. Oracle数据库的逻辑结构和存储层次
  3. while循环的习题
  4. druid连接池配置数据库密码加密
  5. ubuntu设置自动休眠
  6. [react] 你对immutable有了解吗?它有什么作用?
  7. 普通人如何站在时代风口学好AI?这是我看过最好的答案
  8. http响应返回的状态码
  9. 无线接入控制服务器(ac),网络ac和ap是什么意思(图文)
  10. SVN安装和使用详细教程
  11. 基于keras+VGG-16的小数据集多分类图像识别(附代码数据集)
  12. 4G模块的GPS定位差距过大
  13. android 自动剪裁图片,android使用系统裁剪图片
  14. EPMS System Analysis——AD验证、权限设计
  15. openGL glColor3f函数颜色
  16. 山东省农村信用计算机社考试,山东省农村信用社计算机专业考试题.doc
  17. Linux学习-文件操作和属性
  18. xxl-job配置发邮件 spring.mail的QQ邮箱配置
  19. win10如何调整计算机时间同步,win10系统调整设置时间同步的频率的操作方法
  20. python画圣诞树【方块圣诞树、线条圣诞树、豪华圣诞树】

热门文章

  1. 从客户端检测到有潜在危险的Request.Form值的asp.net代码
  2. C++成员函数中的const修饰符
  3. UCOSIIIAPI函数接口OSTaskCreate();函数
  4. 搭建DVWA漏洞环境
  5. docker基础介绍
  6. Java中static关键字有什么用?
  7. 处理器在实施任务切换时的操作——《x86汇编语言:从实模式到保护模式》读书笔记39
  8. spring ioc的包的扫描(基于注解)
  9. 日常生活小技巧 -- markdown编辑器
  10. 区块链 PoS 共识——Tendermint