解决快速索引栏挤压的问题


前段时间遇到快速索引栏被挤压的问题,就做了个demo来研究。
先描述下问题,就是就是在一个联系人的界面中,布局中有列表,一个展示联系人,一个做为字母快速索引。
现在的问题是,
当在搜索框输入时,由于弹出软键盘,导致整体布局上移,从而出现快速索引栏被挤压,导致各字母互相重叠,界面乱了。
如下图:


布局文件是这样的:

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent" >

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ll_find">

<TextView
android:id="@+id/tv_find"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="搜索:"/>
<EditText
android:id="@+id/et_find_contact"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="请输入联系人"
android:layout_toRightOf="@id/tv_find"
/>
</LinearLayout>

<LinearLayout
android:id="@+id/city_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@id/ll_find"
android:orientation="horizontal" >

<ListView android:id="@+id/list_view"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:scrollbars="vertical"
android:cacheColorHint="#00000000" />
<com.droid.MyLetterListView
android:id="@+id/MyLetterListView01"
android:background="#40000000"
android:layout_width="30dip"
android:layout_height="wrap_content"
android:layout_alignParentRight="true" />

</LinearLayout>

</LinearLayout>
MyLetterListView是一个自定义View,是网上一个前辈做的。
实现代码如下:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.style.TypefaceSpan;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class MyLetterListView extends View {
OnTouchingLetterChangedListener onTouchingLetterChangedListener;
String[] b = {"#","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"};
int choose = -1;
Paint paint = new Paint();
boolean showBkg = false;
public MyLetterListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyLetterListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyLetterListView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(showBkg){
   canvas.drawColor(Color.parseColor("#40000000"));
}
   int height = getHeight();
   int width = getWidth();
   int singleHeight = height / b.length;
   for(int i=0;i<b.length;i++){
      paint.setColor(Color.WHITE);
      paint.setTypeface(Typeface.DEFAULT_BOLD);
      paint.setAntiAlias(true);
      if(i == choose){
      paint.setColor(Color.parseColor("#3399ff"));
      paint.setFakeBoldText(true);
      }
      float xPos = width/2  - paint.measureText(b[i])/2;
      float yPos = singleHeight * i + singleHeight;
      canvas.drawText(b[i], xPos, yPos, paint);
      paint.reset();
   }   
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
   final float y = event.getY();
   final int oldChoose = choose;
   final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
   final int c = (int) (y/getHeight()*b.length);
   
switch (action) {
case MotionEvent.ACTION_DOWN:
showBkg = true;
if(oldChoose != c && listener != null){
if(c > 0 && c< b.length){
listener.onTouchingLetterChanged(b[c]);
choose = c;
invalidate();
}
}
break;
case MotionEvent.ACTION_MOVE:
if(oldChoose != c && listener != null){
if(c > 0 && c< b.length){
listener.onTouchingLetterChanged(b[c]);
choose = c;
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
showBkg = false;
choose = -1;
invalidate();
break;
}
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
public void setOnTouchingLetterChangedListener(
OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
}
public interface OnTouchingLetterChangedListener{
public void onTouchingLetterChanged(String s);
}
}

我想到的第一个方法,是保持一个最小高度,以避免字母互相叠加导致看不清了。
可是直接可是布局文件中设置minHeigth,并没有效果。
那么就应该是这个值没有在自定义View中使用。
解决办法,就是在 onDraw() 方法里面,获取在xml布局中设置的最小高度值:
int minHeight = getSuggestedMinimumHeight();
在计算View的高度时,添加上这个限制,保证无论怎么切换,都保持高度值大于或等于最小高度,则可以避免互相重叠的问题了。
这个最小值,通过两三次运行调试,就可以大致确定出来,可以保证每个字母都能完整显示。
但是,实测发现,最初的显示高度,与弹出键盘后设置的最小高度值不一致,从而发现字母快速索引栏在变化,感觉有些不自然。
如何让界面显得很自然呢?最好是感觉不到字母快速索引栏的变化!
怎么办?
也很简单,就是记忆住View在第一次展示时的高度值,后面沿用即可。
主要是修改 onDraw()与dispatchTouchEvent() ,修改后的代码如下:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.style.TypefaceSpan;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class MyLetterListView extends View {
OnTouchingLetterChangedListener onTouchingLetterChangedListener;
String[] b = {"#","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"};
int choose = -1;
Paint paint = new Paint();
boolean showBkg = false;
int listViewHeight=0;
public MyLetterListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyLetterListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyLetterListView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(showBkg){
   canvas.drawColor(Color.parseColor("#40000000"));
}
int height = getHeight();// 获取对应高度
int width = getWidth();// 获取对应宽度
int minHeight = getSuggestedMinimumHeight();
LogUtil.logWithMethod(new Exception(),"height="+height+" minHeight="+minHeight);
if(false) {
//会出现字母索引挤压问题--弹出软键盘时
listViewHeight = height;
} else {
//不会出现字母索引挤压问题
//保证最小高度值
if (height < minHeight) {
height = minHeight;
} else {
// listViewHeight = height;
}
//记住获得的最大高度值,只增不减
if (listViewHeight < height) {
listViewHeight = height;
}
}
LogUtil.logWithMethod(new Exception(),"listViewHeight="+listViewHeight);
int singleHeight = listViewHeight / b.length;// 获取每一个字母的高度
// int singleHeight = getHeight() / b.length;// 获取每一个字母的高度
   for(int i=0;i<b.length;i++){
      paint.setColor(Color.WHITE);
paint.setTextSize(30);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setAntiAlias(true);
// 选中的状态
if (i == choose) {
paint.setColor(Color.parseColor("#3399ff"));
paint.setFakeBoldText(true);
}
// x坐标等于中间-字符串宽度的一半.
float xPos = width / 2 - paint.measureText(b[i]) / 2;
float yPos = singleHeight * i + singleHeight;
canvas.drawText(b[i], xPos, yPos, paint);
paint.reset();// 重置画笔
   }   
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float y = event.getY();// 点击y坐标
final int oldChoose = choose;
final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
// final int c = (int) (y / getHeight() * b.length);// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数.
final int c = (int) ( (y* b.length) / listViewHeight );// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数.
LogUtil.logWithMethod(new Exception(),"y="+y+" b.length="+b.length+" listViewHeight="+listViewHeight);
switch (action) {
case MotionEvent.ACTION_DOWN:
showBkg = true;
if (oldChoose != c && listener != null) {
if (c > 0 && c < b.length) {
LogUtil.logWithMethod(new Exception(),"c="+c);
listener.onTouchingLetterChanged(b[c]);
choose = c;
invalidate();
}
}
break;
case MotionEvent.ACTION_MOVE:
if (oldChoose != c && listener != null) {
if (c > 0 && c < b.length) {
listener.onTouchingLetterChanged(b[c]);
choose = c;
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
showBkg = false;
choose = -1;
invalidate();
break;
}
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
public void setOnTouchingLetterChangedListener(
OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
}
public interface OnTouchingLetterChangedListener{
public void onTouchingLetterChanged(String s);
}
}
这样,就完美的解决了字母挤压的问题了。
不过,后来又发现了另外的一种解决方案,并且非常简单!
就是上面的xml布局文件中,使用RelativeLayout 代替 LinearLayout 即可。
修改后的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ll_find">
<TextView
android:id="@+id/tv_find"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="搜索:"/>
<EditText
android:id="@+id/et_find_contact"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="请输入联系人"
android:layout_toRightOf="@id/tv_find"
/>
</LinearLayout>
<RelativeLayout 
android:orientation="vertical"  
android:layout_width="fill_parent"  
android:layout_height="fill_parent" > 
 
<ListView android:id="@+id/list_view" 
android:layout_height="wrap_content" 
android:layout_width="fill_parent"
android:scrollbars="none"
android:cacheColorHint="#00000000" />
<com.droid.MyLetterListView 
android:id="@+id/MyLetterListView01" 
android:background="#40000000" 
android:layout_width="30dip" 
android:layout_height="fill_parent"
android:layout_alignParentRight="true" />
</RelativeLayout> 
</LinearLayout>
完整的工程,见如下地址:
http://download.csdn.net/download/lintax/10043708
参考:
http://blog.csdn.net/zpp119/article/details/7976139
http://www.javaapk.com/topics/demo/5894.html
http://www.2cto.com/kf/201311/258190.html
总结一下,本demo相比较于原型,有几点改进:
1,两个列表View的布局,不能互相重叠,从而避免点击快速索引栏时左侧的联系人被点击;
2,点击输入框,弹出软键盘时,索引ListView的压缩,导致字符重叠;
1,使用最小高度值,以保护,避免字符重叠;
2,记忆弹出软键盘之前的高度值,视觉效果上保持一致;
3,使用开关,能切换使用手机的联系人或读取一个固定的联系人文件(方便演示与测试);
4,使用开关,可切换到挤压状态。


猜你喜欢

转载自blog.csdn.net/lintax/article/details/78384591
今日推荐