先说一下需求,【自定义可滑动评分拉杆】,
如下图(完成后的效果图):
看到这个需求,第一反应是自定义SeekBar , 因为这样相对于自己重新写一个可省去很多功夫。
其实很简单: 直接使用原生SeekBar,替换它的滑块、及背景图。
这么做呢是没有什么问题,但当你实际替换的时候,你会发现滑块可以替换,但背景图替换后会出现问题:
杆状背景图无法充满,滑块与滑杆之间有距离(如图):
这是因为,原生SeekBar的滑块是个圆形球状,它的直径要比我们要替换的滑块宽很多,所以才会留下缝隙,导致无法充满。
网上说要设置什么SeekBar的各种属性,我都试了没毛用,
所以我就偷个懒,直接将SeekBar的背景设置成透明的,然后再将自己的彩虹背景图替换到同等位置拉伸充满就OK了。
事实证明,我偷懒的功夫还是可以的,这样做出来完全没毛病(请参照完成效果图)。
然后就是滑块上面的Popup了,当初是想做成Popup的,但一想太费工夫效率也不高,所有就又想偷懒了。
我的思路是,直接将这个Popup写成一个Layout,
给Layou设置一个背景图,然后将这个Layout置于滑块正上方给一个初始位置,滑动的时候再去计算位置就OK了。
事实证明,我偷懒的功夫还是可以的,这样做出来完全没毛病(请参照完成效果图)。
代码贴出来,这里主要就是 Item 的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="15dp"
android:background="@color/white">
<TextView
android:id="@+id/scoreName"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:textSize="16sp"
tools:text="技术能力"
android:textColor="@color/black"
android:singleLine="true"
android:layout_marginTop="28.5dp"/>
<TextView
android:id="@+id/scoreValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
tools:text="3.5分"
android:textColor="@color/blue2"
android:layout_alignParentRight="true"
android:layout_marginTop="28.5dp"/>
<RelativeLayout
android:id="@+id/mTopLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginLeft="60dp">
<TextView
android:id="@+id/mTopTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="很差"
android:textSize="14sp"
android:textColor="@color/im_blue"
android:background="@drawable/icon_msg"
android:gravity="center"
android:paddingBottom="2dp"/>
</RelativeLayout>
<LinearLayout
android:id="@+id/seekLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/scoreName"
android:layout_toLeftOf="@id/scoreValue"
android:layout_centerVertical="true"
android:orientation="vertical"
android:layout_below="@id/mTopLayout">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="10dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="12dp"
android:src="@drawable/jihui_bg_pingfen"
android:layout_centerVertical="true"
android:scaleType="fitXY"/>
<SeekBar
android:id="@+id/mSeekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:thumb="@drawable/my_seekbar_thumb"
android:progressDrawable="@drawable/my_seeckbar_background"
android:max="50"
android:splitTrack="false"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="5dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="10dp">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="@color/gray_font"
android:textSize="14sp"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"
android:textColor="@color/gray_font"
android:textSize="14sp"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2"
android:textColor="@color/gray_font"
android:textSize="14sp"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="3"
android:textColor="@color/gray_font"
android:textSize="14sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="4"
android:textColor="@color/gray_font"
android:textSize="14sp"
android:layout_alignParentRight="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="5"
android:textColor="@color/gray_font"
android:textSize="14sp"
android:layout_alignParentRight="true"/>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
彩虹条背景图android:src="@drawable/jihui_bg_pingfen",可根据需求自己替换;
滑块图片android:thumb="@drawable/my_seekbar_thumb",可根据需求自己替换;
SeekBar透明背景android:progressDrawable="@drawable/my_seeckbar_background"如下:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<solid android:color="@color/transparent" />
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<solid android:color="@color/transparent" />
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="@color/transparent" />
</shape>
</clip>
</item>
</layer-list>
OK,到这里我们的布局基本完成,接下来就是要计算这个Popup的位置了。
当SeekBar的滑块滑动的时候,Popup要跟随滑块滑动,并随着滑动区域不同而显示不同的文字。
既然Popup写成了Layout,我们就通过setLayoutParams来改变Popup的位置,
那位置怎么计算,重点来了:
SeekBar的实际宽度 / SeekBar的最大值 = SeekBar最小单位的实际滑动距离
SeekBar当前值 * SeekBar最小单位的实际滑动距离 = Popup距左侧的实际距离
看懂这两个公式,其实就很简单了,
最后一步setLayoutParams(layoutParams)就OK了
贴一下代码(以下代码在adapter中的getVeiw中):
//设置SeekBar的初始值
if(firstIn.equals("0")){ //防止循环设置seekBar的progress
scoreValue.setText(Float.valueOf(mScore)+"分");
mSeekBar.setProgress((int) (Float.valueOf(mScore) * 10));
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//延时设置Popup的初始位置
setSeekBarLocation(mSeekBar,(int) (Float.valueOf(mScore) * 10),mTopTextView);
}
},200);
}
//seekBar滑动监听
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int value, boolean b) {
float mValue = (float) value / 10;
scoreValue.setText(mValue+"分");
setSeekBarLocation(seekBar,value,mTopTextView);//每次滑动都设置一遍Popup的位置
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
firstIn = "1";
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
//设置Popup的位置
private void setSeekBarLocation(SeekBar seekBar,int value,TextView mTopTextView){
if(mWidth == 0){
mWidth = seekBar.getWidth();//SeekBar的实际宽度
movePixes = ((float) mWidth / (float) seekBar.getMax());
}
int newLeftMargin = (int) (movePixes * seekBar.getProgress());//SeekBar滑块距左侧的距离
//设置不同区域的显示文字
if(value >= 0 && value <= 20){
mTopTextView.setText("很差");
}else if(value >20 && value < 25){
mTopTextView.setText("不合格");
}else if(value >=25 && value <= 30){
mTopTextView.setText("合格");
}else if(value >30 && value < 40){
mTopTextView.setText("良好");
}else if(value >= 40){
mTopTextView.setText("优秀");
}
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mTopTextView.getLayoutParams();
layoutParams.leftMargin = newLeftMargin;
mTopTextView.setLayoutParams(layoutParams);//设置Popup的位置
}
OK,到这里就大功告成了。