引言
这周前半部分的工作我们主要对页面进行了初步设计,也实现了一些功能代码,后半部分在页面方面进行了进一步的详细设计,并且编写了其他功能代码,总结一下工作目录:
- 页面详细设计与所有页面基本完成
- 客户端代码编写
- 后台代码编写
在这里,我对自己主要从事的工作,即客户端代码的编写,具体来说是查看好友界面及内部处理逻辑的编写,做一个着重的介绍。
一、界面编写
在之前的界面设计中,设计出的界面如下所示
经过分析,发现该界面的最主要的内容是好友的列表,因此考虑使用两个嵌套的ListView组件实现。但是,用户的分组是通过字母进行的,故外层的ListView实现起来会比较复杂。因此,我考虑自定义一个LetterFilterListView控件,继承ListView,实现了用侧边字母进行好友检索的功能,代码如下:
package com.example.sdu.myflag.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.SectionIndexer; /** * 字母控件 */ @SuppressWarnings("ResourceType") public class LetterFilterListView extends RelativeLayout { /** * The context. */ private Context mContext; /** * The section indexter. */ private SectionIndexer mSectionIndexter = null; /** * The list view. */ private ListView mListView; /** * The letter view. */ private LetterView mLetterView; /** * The selectedLetter view */ private LetterSelectedView mLetterSelectedView; public LetterFilterListView(Context context) { this(context, null); } public LetterFilterListView(Context context, AttributeSet attrs) { this(context, null, 0); } public LetterFilterListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { this.mContext = context; } @Override protected void onFinishInflate() { super.onFinishInflate(); int count = getChildCount(); /** 必须包括一个子 View */ if (count < 1) { throw new IllegalArgumentException("this layout must contain 1 child views,and AdapterView must in the first position!"); } View view = this.getChildAt(0); AdapterView<?> adapterView = null; if (view instanceof AdapterView<?>) { adapterView = (AdapterView<?>) view; mListView = (ListView) adapterView; mSectionIndexter = (SectionIndexer) mListView.getAdapter(); /** 右边的字母显示 */ mLetterView = new LetterView(mContext); mLetterView.setListView(mListView); mLetterView.setId(5000);//为了下面的控件布局 RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(60, ViewGroup.LayoutParams.WRAP_CONTENT); layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE); layoutParams.topMargin = 10; layoutParams.rightMargin = 10; layoutParams.bottomMargin = 10; this.addView(mLetterView, layoutParams); /** 点击右边的字母后显示选中的字母 */ mLetterSelectedView = new LetterSelectedView(mContext); RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(30, ViewGroup.LayoutParams.WRAP_CONTENT); layoutParams1.topMargin = 10; layoutParams1.rightMargin = 10; layoutParams1.bottomMargin = 10; layoutParams1.addRule(RelativeLayout.LEFT_OF, mLetterView.getId()); this.addView(mLetterSelectedView, layoutParams1); } /** 必须包括一个子 AdapterView,一般就是 ListView */ if (adapterView == null) { throw new IllegalArgumentException("must contain a AdapterView in this layout!"); } } /** * 右边字母显示 View * * @author liuyinjun * @date 2015-3-16 */ private class LetterView extends View { /** * The list. */ private ListView mListView; /** * The letter. */ private char[] mLetter; /** * The paint. */ private Paint mPaint; /** * The width center. */ private float mWidthCenter; /** * 字母之间的间距. */ private float mSingleHeight; public LetterView(Context context) { this(context, null); } public LetterView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public LetterView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } /** * 初始化. */ private void init() { mLetter = new char[]{'#', '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'}; mPaint = new Paint(); mPaint.setColor(Color.parseColor("#949494")); mPaint.setTypeface(Typeface.DEFAULT_BOLD); mPaint.setTextSize(22); mPaint.setAntiAlias(true); mPaint.setTextAlign(Paint.Align.CENTER); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float height = getHeight(); mSingleHeight = height / mLetter.length; mWidthCenter = getMeasuredWidth() / (float) 2; for (int i = 0; i < mLetter.length; i++) { canvas.drawText(String.valueOf(mLetter[i]), mWidthCenter, mSingleHeight + (i * mSingleHeight), mPaint); } } /** * Gets the list view. * * @return the list view */ public ListView getListView() { return mListView; } /** * Sets the list view. * * @param listView the new list view */ public void setListView(ListView listView) { this.mListView = listView; } public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); int index = 0;//点击的位置在 mLetters 中的索引 int i = (int) event.getY(); int div = (int) mSingleHeight; /** 重新计算出索引 */ if (div != 0) { index = i / div; } if (index >= mLetter.length) { index = mLetter.length - 1; } else if (index < 0) { index = 0; } switch (event.getAction()) { case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: mLetterSelectedView.setViewY(mSingleHeight + (index * mSingleHeight));//设置选中字母显示的Y轴坐标位置 mLetterSelectedView.setSelectedLetter(mLetter[index]);//设置选中的字母 if (mLetterSelectedView.getVisibility() == View.GONE)//若控件为隐藏状态,则显示 mLetterSelectedView.setVisibility(View.VISIBLE); /** 显示1s后消失*/ mLetterSelectedView.postDelayed(new Runnable() { @Override public void run() { // TODO Auto-generated method stub mLetterSelectedView.setVisibility(View.GONE); } }, 1000); if (mListView.getAdapter() != null) { ListAdapter listAdapter = (ListAdapter) mListView.getAdapter(); if (mSectionIndexter == null) { mSectionIndexter = (SectionIndexer) listAdapter; } int position = mSectionIndexter.getPositionForSection(mLetter[index]); if (position == -1) {//列表中没有首字母为选中字母的的项 return true; } mListView.setSelection(position); } } return true; } } /** * 点击右边字母后显示的出的加大字母 * * @author liuyinjun * @date 2015-3-16 */ private class LetterSelectedView extends View { private Paint mPaint; /** * 选中的字母 */ private char mLetter = 'A'; /** * 绘制字母的Y轴坐标 */ private float mY; public LetterSelectedView(Context context) { this(context, null); } public LetterSelectedView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public LetterSelectedView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { mPaint = new Paint(); mPaint.setColor(Color.parseColor("#ff55bb22")); mPaint.setTypeface(Typeface.DEFAULT_BOLD); mPaint.setTextSize(35); mPaint.setAntiAlias(true); mPaint.setTextAlign(Paint.Align.CENTER); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawText(String.valueOf(mLetter), getMeasuredWidth() / (float) 2, mY, mPaint); } public void setSelectedLetter(char letter) { this.mLetter = letter; this.invalidate();//刷新控件 } public void setViewY(float y) { this.mY = y; } } }
可以看到,该类继承自ListView,自然具有ListView的功能,又重写了父类的方法,增加了按照首字母进行排序的功能。这样一来,使用该自定义类就可自动完成按照首字母的排序要求。
在此基础上,写出的xml布局文件如下所示。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#fffafafa" android:orientation="vertical" > <RelativeLayout android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/white"> <ImageButton android:id="@+id/back_btn" android:layout_width="?attr/actionBarSize" android:layout_height="?attr/actionBarSize" android:layout_alignParentLeft="true" android:onClick="FriendBack" android:background="@drawable/toolbar_back_bg" android:src="?attr/homeAsUpIndicator" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="好友" android:textColor="@color/black" android:textSize="19sp" /> <Button android:id="@+id/complete_btn" android:layout_width="?attr/actionBarSize" android:layout_height="?attr/actionBarSize" android:layout_alignParentRight="true" android:text="完成" android:textSize="15sp" android:textColor="@color/login_button_default_blue" android:background="@drawable/toolbar_back_bg" android:src="?attr/homeAsUpIndicator" /> </RelativeLayout> <com.example.sdu.myflag.widget.LetterFilterListView android:id="@+id/letterView" android:layout_width="wrap_content" android:layout_height="fill_parent" android:focusable="true" android:focusableInTouchMode="true"> <ListView android:id="@+id/listView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gravity="center" android:cacheColorHint="#00000000" android:divider="@null"/> </com.example.sdu.myflag.widget.LetterFilterListView> </LinearLayout>
二、内部处理逻辑代码编写
在之前的详细设计中,已经完成了该功能的详细设计,UML活动图如下所示
根据详细设计,定义了类FriendActivity,具体代码如下所示:
package com.example.sdu.myflag.activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.ListView; import android.widget.Toast; import com.example.sdu.myflag.R; import com.example.sdu.myflag.adapter.FriendListAdapter; import com.example.sdu.myflag.base.BaseActivity; import com.example.sdu.myflag.base.BaseApplication; import com.example.sdu.myflag.bean.FriendBean; import com.example.sdu.myflag.util.CharacterParser; import com.example.sdu.myflag.util.NetUtil; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import okhttp3.Response; /** * 好友列表界面 */ public class FriendActivity extends BaseActivity { private ListView listView; private FriendListAdapter friendListAdapter; int code; Button complete; @Override public int getLayoutId() { return R.layout.activity_friend; } @Override public void afterCreate(Bundle savedInstanceState) { listView = (ListView) findViewById(R.id.listView); code = getIntent().getIntExtra("code", 0); complete = (Button) findViewById(R.id.complete_btn); complete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.putExtra("selected", friendListAdapter.getSelected()); setResult(1, intent); FriendActivity.this.finish(); } }); if (code == 0) complete.setVisibility(View.GONE); getData(); } private void getData() { SharedPreferences sp = BaseApplication.getInstance().getSharedPreferences("User", MODE_PRIVATE); String id = sp.getString("uid", ""); ArrayList<NetUtil.Param> params = new ArrayList<>(); params.add(new NetUtil.Param("id", id)); try { NetUtil.getResult(NetUtil.getFriendListUrl, params, new FriendListCallBack()); } catch (IOException e) { e.printStackTrace(); } } public void FriendBack(View view) { this.finish(); } class FriendListCallBack implements NetUtil.CallBackForResult { @Override public void onFailure(IOException e) { FriendActivity.this.runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(FriendActivity.this, "获取好友列表失败", Toast.LENGTH_SHORT).show(); } }); } @Override public void onSuccess(final Response response) { if (response.isSuccessful()) { try { final String res = response.body().string(); FriendActivity.this.runOnUiThread(new Runnable() { @Override public void run() { JSONArray jsonArray = null; try { JSONObject friend = new JSONObject(res); jsonArray = friend.getJSONArray("friend"); ArrayList<FriendBean> list = new ArrayList<>(); CharacterParser characterParser = new CharacterParser(); for (int i = 0; i < jsonArray.length(); i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); String nickname = jsonObject.optString("nickname"); String remark = jsonObject.optString("remark"); String uid = jsonObject.optString("uid"); int iconId = jsonObject.optInt("photo"); String pinYin = characterParser.getSelling(nickname); String sortString = pinYin.substring(0, 1).toUpperCase(); String firstLetter = ""; if (sortString.matches("[A-Z]")) { firstLetter = sortString.toUpperCase(); } else { firstLetter = "#"; } list.add(new FriendBean(uid, nickname, firstLetter, remark, iconId)); } Collections.sort(list); friendListAdapter = new FriendListAdapter(FriendActivity.this, list, code); listView.setAdapter(friendListAdapter); } catch (JSONException e) { e.printStackTrace(); } } }); } catch (IOException e) { e.printStackTrace(); } } } } }
三、总结
总体来说,这两天的还是不错的,虽然只实现了一个功能,但毕竟对ListView组件进行了重写,还是有一定的工作量的。好友也算一个比较复杂的部分,解决了好友功能,相信之后的工作会更加顺利。