一个类似MIUI中原生短信编辑功能

首先描述下需求:

1、新增短信时,进来收件人是为空,显示一行文字(提醒)

2、从通讯录选择联系人后,回到短信编辑界面,收件栏显示一行,内容为“收件人:XXX、XXX、XXX、XXX.....”

3、当点击收件栏时,收件栏内容变化,变成可删除,最多显示四行,多余四行有上下滑动轮,不足四行,是几行显示几行

4、填写短信内容,即时计算短信条算,并有文本提示

下面是实现后效果图

下面终于到了代码实现块了,主要写一些主要用到的类,其他不太重要的就忽略写,读者自行补全。

首先是短信收件人是一个自定义的流布局,这个流布局不是本人写的,只是在上面加了些修改来满足自己的业务需求。

TagFlowLayout类

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.zhaonongzi.wnshhseller.R;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
 * 自定义tag 页面的整体页面
 *
 * 实现绑定数据,事件处理
 * @author admin
 *
 */
public class TagFlowLayout extends FlowLayout implements
		TagAdapter.OnDataChangedListener {

	private TagAdapter mTagAdapter;

	private boolean mAutoSelectEffect = true;
	private int mSelectedMax = -1;// -1为不限制数量
	private static final String TAG = "TagFlowLayout";
	private MotionEvent mMotionEvent;

	private Set<Integer> mSelectedView = new HashSet<Integer>();

	public TagFlowLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		TypedArray ta = context.obtainStyledAttributes(attrs,
				R.styleable.TagFlowLayout);
		mAutoSelectEffect = ta.getBoolean(R.styleable.TagFlowLayout_auto_select_effect, true);
		mSelectedMax = ta.getInt(R.styleable.TagFlowLayout_max_select, -1);
		ta.recycle();

		if (mAutoSelectEffect) {
			setClickable(true);
		}
	}

	public TagFlowLayout(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public TagFlowLayout(Context context) {
		this(context, null);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int cCount = getChildCount();

		for (int i = 0; i < cCount; i++) {
			TagView tagView = (TagView) getChildAt(i);
			if (tagView.getVisibility() == View.GONE)
				continue;
			if (tagView.getTagView().getVisibility() == View.GONE) {
				tagView.setVisibility(View.GONE);
			}
		}
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}

	public interface OnSelectListener {
		void onSelected(Set<Integer> selectPosSet);
	}

	private OnSelectListener mOnSelectListener;

	public void setOnSelectListener(OnSelectListener onSelectListener) {
		mOnSelectListener = onSelectListener;
		if (mOnSelectListener != null)
			setClickable(true);
	}

	public interface OnTagClickListener {
		boolean onTagClick(View view, int position, FlowLayout parent);
	}

	private OnTagClickListener mOnTagClickListener;

	public void setOnTagClickListener(OnTagClickListener onTagClickListener) {
		mOnTagClickListener = onTagClickListener;
		if (onTagClickListener != null)
			setClickable(true);
	}

	public void setAdapter(TagAdapter adapter) {
		// if (mTagAdapter == adapter)
		// return;
		mTagAdapter = adapter;
		mTagAdapter.setOnDataChangedListener(this);
		changeAdapter();

	}

	private void changeAdapter() {
		removeAllViews();
		TagAdapter adapter = mTagAdapter;
		TagView tagViewContainer = null;
		HashSet preCheckedList = mTagAdapter.getPreCheckedList();
		for (int i = 0; i < adapter.getCount(); i++) {
			View tagView = adapter.getView(this, i, adapter.getItem(i));

			tagViewContainer = new TagView(getContext());
			// ViewGroup.MarginLayoutParams clp = (ViewGroup.MarginLayoutParams)
			// tagView.getLayoutParams();
			// ViewGroup.MarginLayoutParams lp = new
			// ViewGroup.MarginLayoutParams(clp);
			// lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
			// lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
			// lp.topMargin = clp.topMargin;
			// lp.bottomMargin = clp.bottomMargin;
			// lp.leftMargin = clp.leftMargin;
			// lp.rightMargin = clp.rightMargin;
			tagView.setDuplicateParentStateEnabled(true);
			tagViewContainer.setLayoutParams(tagView.getLayoutParams());
			tagViewContainer.addView(tagView);
			addView(tagViewContainer);

			if (preCheckedList.contains(i)) {
				tagViewContainer.setChecked(true);
			}
		}
		mSelectedView.addAll(preCheckedList);

	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (event.getAction() == MotionEvent.ACTION_UP) {
			mMotionEvent = MotionEvent.obtain(event);
		}
		return super.onTouchEvent(event);
	}

	@Override
	public boolean performClick() {
		if (mMotionEvent == null)
			return super.performClick();

		int x = (int) mMotionEvent.getX();
		int y = (int) mMotionEvent.getY();
		mMotionEvent = null;

		TagView child = findChild(x, y);
		int pos = findPosByView(child);
		if (child != null) {
			doSelect(child, pos);
			if (mOnTagClickListener != null) {
				return mOnTagClickListener.onTagClick(child.getTagView(), pos,
						this);
			}
		}
		return super.performClick();
	}

	public void setMaxSelectCount(int count) {
		if (mSelectedView.size() > count) {
			Log.w(TAG, "you has already select more than " + count
					+ " views , so it will be clear .");
			mSelectedView.clear();
		}
		mSelectedMax = count;
	}

	public Set<Integer> getSelectedList() {
		return new HashSet<Integer>(mSelectedView);
	}

	private void doSelect(TagView child, int position) {
		if (mAutoSelectEffect) {
			if (!child.isChecked()) {
				// 处理max_select=1的情况
				if (mSelectedMax == 1 && mSelectedView.size() == 1) {
					Iterator<Integer> iterator = mSelectedView.iterator();
					Integer preIndex = iterator.next();
					TagView pre = (TagView) getChildAt(preIndex);
					pre.setChecked(false);
					child.setChecked(true);
					mSelectedView.remove(preIndex);
					mSelectedView.add(position);
				} else {
					if (mSelectedMax > 0
							&& mSelectedView.size() >= mSelectedMax)
						return;
					child.setChecked(true);
					mSelectedView.add(position);
				}
			} else {
				child.setChecked(false);
				mSelectedView.remove(position);
			}
			if (mOnSelectListener != null) {
				mOnSelectListener
						.onSelected(new HashSet<Integer>(mSelectedView));
			}
		}
	}

	private static final String KEY_CHOOSE_POS = "key_choose_pos";
	private static final String KEY_DEFAULT = "key_default";

	@Override
	protected Parcelable onSaveInstanceState() {
		Bundle bundle = new Bundle();
		bundle.putParcelable(KEY_DEFAULT, super.onSaveInstanceState());

		String selectPos = "";
		if (mSelectedView.size() > 0) {
			for (int key : mSelectedView) {
				selectPos += key + "|";
			}
			selectPos = selectPos.substring(0, selectPos.length() - 1);
		}
		bundle.putString(KEY_CHOOSE_POS, selectPos);
		return bundle;
	}

	@Override
	protected void onRestoreInstanceState(Parcelable state) {
		if (state instanceof Bundle) {
			Bundle bundle = (Bundle) state;
			String mSelectPos = bundle.getString(KEY_CHOOSE_POS);
			if (!TextUtils.isEmpty(mSelectPos)) {
				String[] split = mSelectPos.split("\\|");
				for (String pos : split) {
					int index = Integer.parseInt(pos);
					mSelectedView.add(index);

					TagView tagView = (TagView) getChildAt(index);
					tagView.setChecked(true);
				}

			}
			super.onRestoreInstanceState(bundle.getParcelable(KEY_DEFAULT));
			return;
		}
		super.onRestoreInstanceState(state);
	}

	private int findPosByView(View child) {
		final int cCount = getChildCount();
		for (int i = 0; i < cCount; i++) {
			View v = getChildAt(i);
			if (v == child)
				return i;
		}
		return -1;
	}

	private TagView findChild(int x, int y) {
		final int cCount = getChildCount();
		for (int i = 0; i < cCount; i++) {
			TagView v = (TagView) getChildAt(i);
			if (v.getVisibility() == View.GONE)
				continue;
			Rect outRect = new Rect();
			v.getHitRect(outRect);
			if (outRect.contains(x, y)) {
				return v;
			}
		}
		return null;
	}

	@Override
	public void onChanged() {
		changeAdapter();
	}

}

FlowLayout类

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

import com.zhaonongzi.wnshhseller.activity.customer.AddMessageActivity;
import com.zhaonongzi.wnshhseller.utils.GlobalMemoryCache;

/**
 * Tag 页面布局基类 主要实现测量子view,绘制view
 *
 * @author admin
 *
 */
public class FlowLayout extends ViewGroup {

	protected List<List<View>> mAllViews = new ArrayList<List<View>>();
	protected List<Integer> mLineHeight = new ArrayList<Integer>();
	private int lines, counts, lastItem;// 限制显示多少行
	private boolean isShow;// 是否需要在最后显示省略号
	private LastListerInterface lastListerInterface = null;
	private int ihegth;

	public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public FlowLayout(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public FlowLayout(Context context) {
		this(context, null);
	}

	public void setLines(int line) {
		this.lines = line;
	}

	public int getLastItem() {
		Log.e("lastItem", lastItem + "");
		return lastItem;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
		int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
		int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
		int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

		// wrap_content
		int width = 0;
		int height = 0;

		int lineWidth = 0;
		int lineHeight = 0;
		int childHeight1 = 0;
		int cCount = getChildCount();

		for (int i = 0; i < cCount; i++) {
			View child = getChildAt(i);
			if (child.getVisibility() == View.GONE) {
				if (i == cCount - 1) {
					width = Math.max(lineWidth, width);
					height += lineHeight;
				}
				continue;
			}
			measureChild(child, widthMeasureSpec, heightMeasureSpec);
			MarginLayoutParams lp = (MarginLayoutParams) child
					.getLayoutParams();

			int childWidth = child.getMeasuredWidth() + lp.leftMargin
					+ lp.rightMargin;
			int childHeight = child.getMeasuredHeight() + lp.topMargin
					+ lp.bottomMargin;
			childHeight1 = childHeight;
			if (lineWidth + childWidth > sizeWidth - getPaddingLeft()
					- getPaddingRight()) {
				width = Math.max(width, lineWidth);
				lineWidth = childWidth;
				height += lineHeight;
				lineHeight = childHeight;
			} else {
				lineWidth += childWidth;
				lineHeight = Math.max(lineHeight, childHeight);
			}
			if (i == cCount - 1) {
				width = Math.max(lineWidth, width);
				height += lineHeight;

			}
			if (lastItem == 0 && height == (3 * childHeight)) {
				lastItem = i - 1;
				if (lastItem > 0 && null != lastListerInterface) {
					lastListerInterface.getLastItem(lastItem);
				}
			}

			// Log.e("lastItem", lastItem + "");
		}
		height = (height <= (lines * childHeight1)) ? height
				: (lines * childHeight1);
		ihegth = height;
		AddMessageActivity.iheight = childHeight1;

		setMeasuredDimension(
				//
				modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width
						+ getPaddingLeft() + getPaddingRight(),
				modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height
						+ getPaddingTop() + getPaddingBottom()//
		);

	}

	public int getHeigth() {
		return ihegth;
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		mAllViews.clear();
		mLineHeight.clear();

		int width = getWidth();

		int lineWidth = 0;
		int lineHeight = 0;

		List<View> lineViews = new ArrayList<View>();

		int cCount = getChildCount();

		for (int i = 0; i < cCount; i++) {
			View child = getChildAt(i);
			if (child.getVisibility() == View.GONE)
				continue;
			MarginLayoutParams lp = (MarginLayoutParams) child
					.getLayoutParams();

			int childWidth = child.getMeasuredWidth();
			int childHeight = child.getMeasuredHeight();

			if (childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width
					- getPaddingLeft() - getPaddingRight()) {
				if (mAllViews.size() < lines) {
					mLineHeight.add(lineHeight);
					mAllViews.add(lineViews);

					lineWidth = 0;
					lineHeight = childHeight + lp.topMargin + lp.bottomMargin;
					lineViews = new ArrayList<View>();
				}
			}
			lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
			lineHeight = Math.max(lineHeight, childHeight + lp.topMargin
					+ lp.bottomMargin);
			lineViews.add(child);

		}
		mLineHeight.add(lineHeight);
		mAllViews.add(lineViews);

		int left = getPaddingLeft();
		int top = getPaddingTop();

		int lineNum = mAllViews.size();
		AddMessageActivity.ilines = lineNum;
		if (lines > 0) {
			lineNum = lineNum > lines ? lines : lineNum;
		}
		for (int i = 0; i < lineNum; i++) {
			lineViews = mAllViews.get(i);
			lineHeight = mLineHeight.get(i);

			for (int j = 0; j < lineViews.size(); j++) {
				View child = lineViews.get(j);
				if (child.getVisibility() == View.GONE) {
					continue;
				}

				MarginLayoutParams lp = (MarginLayoutParams) child
						.getLayoutParams();

				int lc = left + lp.leftMargin;
				int tc = top + lp.topMargin;
				int rc = lc + child.getMeasuredWidth();
				int bc = tc + child.getMeasuredHeight();

				child.layout(lc, tc, rc, bc);

				left += child.getMeasuredWidth() + lp.leftMargin
						+ lp.rightMargin;
			}
			left = getPaddingLeft();
			top += lineHeight;
		}
		if ((boolean) GlobalMemoryCache.getInstance().get("addMessage")) {
			AddMessageActivity.tfl_add_message_labellay
					.setVisibility(View.GONE);
			AddMessageActivity.scr_add_message_labellay
					.setVisibility(View.GONE);
		}
	}

	@Override
	public LayoutParams generateLayoutParams(AttributeSet attrs) {
		return new MarginLayoutParams(getContext(), attrs);
	}

	@Override
	protected LayoutParams generateDefaultLayoutParams() {
		return new MarginLayoutParams(LayoutParams.WRAP_CONTENT,
				LayoutParams.WRAP_CONTENT);
	}

	@Override
	protected LayoutParams generateLayoutParams(LayoutParams p) {
		return new MarginLayoutParams(p);
	}

	public interface LastListerInterface {
		public void getLastItem(int lastItem);
	}

	public void setLastListener(LastListerInterface lastListerInterface) {
		this.lastListerInterface = lastListerInterface;
	}
}

TagAdapter类

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

import android.view.View;

/**
 * tag 布局适配器
 * @author admin
 *
 * @param <T>
 */
public abstract class TagAdapter<T>
{
    private List<T> mTagDatas;
    private OnDataChangedListener mOnDataChangedListener;
    private HashSet<Integer> mCheckedPosList = new HashSet<Integer>();

    public TagAdapter(List<T> datas)
    {
        mTagDatas = datas;
    }

    public TagAdapter(T[] datas)
    {
        mTagDatas = new ArrayList<T>(Arrays.asList(datas));
    }

	static interface OnDataChangedListener
    {
        void onChanged();
    }

    void setOnDataChangedListener(OnDataChangedListener listener)
    {
        mOnDataChangedListener = listener;
    }

    public void setSelectedList(int... pos)
    {
        for (int i = 0; i < pos.length; i++)
            mCheckedPosList.add(pos[i]);
        notifyDataChanged();
    }

    HashSet<Integer> getPreCheckedList()
    {
        return mCheckedPosList;
    }

    public int getCount()
    {
        return mTagDatas == null ? 0 : mTagDatas.size();
    }

    public void notifyDataChanged()
    {
        mOnDataChangedListener.onChanged();
    }

    public T getItem(int position)
    {
        return mTagDatas.get(position);
    }

    public abstract View getView(FlowLayout parent, int position, T t);

}

TagView类

import android.content.Context;
import android.view.View;
import android.widget.Checkable;
import android.widget.FrameLayout;

/**
 * 单个Tag 的自定义布局
 *
 * @author admin
 *
 */
public class TagView extends FrameLayout implements Checkable
{
    private boolean isChecked;
    private static final int[] CHECK_STATE = new int[]{android.R.attr.state_checked};

    public TagView(Context context)
    {
        super(context);
    }

    public View getTagView()
    {
        return getChildAt(0);
    }

    @Override
    public int[] onCreateDrawableState(int extraSpace)
    {
        int[] states = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked())
        {
            mergeDrawableStates(states, CHECK_STATE);
        }
        return states;
    }

    /**
     * Change the checked state of the view
     *
     * @param checked The new checked state
     */
    @Override
    public void setChecked(boolean checked)
    {
        if (this.isChecked != checked)
        {
            this.isChecked = checked;
            refreshDrawableState();
        }
    }

    /**
     * @return The current checked state of the view
     */
    @Override
    public boolean isChecked()
    {
        return isChecked;
    }

    /**
     * Change the checked state of the view to the inverse of its current state
     */
    @Override
    public void toggle()
    {
        setChecked(!isChecked);
    }

}

上面是用到TagFlowLayout这个自定义控件需要的类。

下面给出短信编辑的布局xml文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tag="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#f5f5f5"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:orientation="vertical" >

    <com.zhaonongzi.wnshhseller.view.TopBar
        android:id="@+id/message_mass_top"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center|top" />

    <LinearLayout
        android:id="@+id/linear_edit_message_add_receiver"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/message_mass_top"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/txt_add_message_customer_declare"
            android:layout_width="wrap_content"
            android:layout_height="45dp"
            android:gravity="center"
            android:paddingLeft="15dp"
            android:text="收信人"
            android:textColor="@color/black"
            android:visibility="gone" />

        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="1" >

            <TextView
                android:id="@+id/txt_add_message_add_customer_btn"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:gravity="left|center"
                android:paddingLeft="15dp"
                android:singleLine="true"
                android:text="点击添加收信客户"
                android:textColor="@color/gray" />

            <ScrollView
                android:id="@+id/scr_add_message_labellay"
                android:layout_width="fill_parent"
                android:layout_height="90dp"
                android:visibility="gone" >

                <com.zhaonongzi.wnshhseller.view.flowlayout.TagFlowLayout
                    android:id="@+id/tfl_add_message_labellay"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/ac_include"
                    android:layout_marginTop="4dp"
                    android:layout_weight="1"
                    android:paddingLeft="10dp"
                    android:paddingRight="10dp"
                    android:visibility="gone"
                    tag:max_select="-1" />
            </ScrollView>
        </FrameLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:paddingLeft="10dp"
            android:paddingRight="15dp"
            android:layout_gravity="center"
            android:paddingTop="5dp"
            android:paddingBottom="5dp">

            <ImageView
                android:id="@+id/img_add_message_add_customer"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_gravity="center"
                android:src="@drawable/username" />

            <TextView
                android:id="@+id/txt_add_message_customer_counts"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:textColor="@color/black"
                android:text="0" />
        </LinearLayout>
    </LinearLayout>

    <View
        android:id="@+id/line_1"
        android:layout_width="fill_parent"
        android:layout_height="1dp"
        android:layout_below="@+id/linear_edit_message_add_receiver"
        android:layout_marginBottom="2dp"
        android:layout_marginTop="2dp"
        android:background="#A0A0A0" />

    <EditText
        android:id="@+id/edittext_add_message_add_content"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/line_1"
        android:background="@drawable/select_white_bg"
        android:minHeight="80dp"
        android:padding="15dp"
        android:maxLength="650"
        android:scrollbars="vertical"
        android:maxHeight="200dp"
        android:textColor="@color/black" />

    <TextView
        android:id="@+id/txt_add_message_add_content_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/edittext_add_message_add_content"
        android:layout_toLeftOf="@+id/txt_add_message_add_content_word"
        android:paddingTop="10dp"
        android:text="0"
        android:textColor="@color/orange_title" />

    <TextView
        android:id="@+id/txt_add_message_add_content_word"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/edittext_add_message_add_content"
        android:paddingRight="15dp"
        android:paddingTop="10dp"
        android:text=""
        android:textColor="@color/gray" />

</RelativeLayout>

实例化一个收件人adapter

adapter = new TagAdapter<String>(meBean.getReceiver()) {
					final LayoutInflater mInflater = LayoutInflater
							.from(AddMessageActivity.this);

					@SuppressLint("NewApi")
					@Override
					public View getView(FlowLayout parent, final int position,
							final String s) {
						final TextView tv = (TextView) mInflater.inflate(
								R.layout.item_label_tv_medium,
								tfl_add_message_labellay, false);
						SpannableStringBuilder sp1 = new SpannableStringBuilder(
								s);// 姓名
						SpannableStringBuilder sp2 = new SpannableStringBuilder(
								"x");// 删除
						sp1.setSpan(
								new ForegroundColorSpan(Color
										.rgb(102, 102, 102)), 0, s.length(),
								Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
						sp2.setSpan(
								new ForegroundColorSpan(Color.rgb(255, 0, 0)),
								0, "X".length(),
								Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

						SpannableStringBuilder sp = new SpannableStringBuilder();//
						sp.append(sp1).append(sp2);

						if (position == 0) {
							tv.setText(s);
							tv.setTextColor(android.graphics.Color
									.parseColor("#666666"));
						} else {
							tv.setText(sp);
							tv.setTextSize(16);
							tv.setBackgroundResource(R.drawable.selector_contacts_gray_btn);
						}
						tv.setOnClickListener(new View.OnClickListener() {

							@Override
							public void onClick(View v) {
								// 跳页面带值
								if (position != 0) {
									meBean.getReceiver().remove(position);
									handler.sendEmptyMessage(1);
								}
							}
						});
						return tv;
					}
				};

计算短信条数方法:

/**
	 * 计算短信内容条算
	 *
	 * @param wordNum
	 * @return
	 */
	public int getSMSCounts(int wordNum) {
		int counts;
		if (wordNum <= 63)
			counts = 1;
		else if (wordNum <= 127)
			counts = 2;
		else {
			counts = (wordNum - 127) % 67 == 0 ? ((wordNum - 127) / 67 + 2)
					: ((wordNum + 7) / 67 + 1);
		}
		return counts;
	}

监听编辑文本框来提示短信条数和字数:

edittext_edit_message_add_content
				.addTextChangedListener(new TextWatcher() {

					@Override
					public void onTextChanged(CharSequence s, int start,
							int before, int count) {
						// TODO Auto-generated method stub

					}

					@Override
					public void beforeTextChanged(CharSequence s, int start,
							int count, int after) {

					}

					@Override
					public void afterTextChanged(Editable s) {
						if (getSMSCounts(edittext_edit_message_add_content
								.getText().toString().trim().length()) == 1) {
							int c = 63 - edittext_edit_message_add_content
									.getText().toString().trim().length();
							txt_add_message_add_content_num.setText(c + "");
							txt_add_message_add_content_word.setText("");
						} else if (getSMSCounts(edittext_edit_message_add_content
								.getText().toString().trim().length()) == 2) {
							int c = 127 - edittext_edit_message_add_content
									.getText().toString().trim().length();
							txt_add_message_add_content_num.setText(c + "");
							txt_add_message_add_content_word.setText("(2)");
						} else if (getSMSCounts(edittext_edit_message_add_content
								.getText().toString().trim().length()) > 2) {
							int c = getSMSCounts(edittext_edit_message_add_content
									.getText().toString().trim().length())
									* 67
									- 7
									- edittext_edit_message_add_content
											.getText().toString().trim()
											.length();
							txt_add_message_add_content_num.setText(c + "");
							txt_add_message_add_content_word
									.setText("("
											+ getSMSCounts(edittext_edit_message_add_content
													.getText().toString()
													.trim().length()) + ")");
						}
					}
				});

接收删除后的handler处理:

private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			if (msg.what == 1) {
				tfl_add_message_labellay.onChanged();
				txt_add_message_customer_counts.setText((meBean.getReceiver()
						.size() - 1) + "");
			}
		};
	};

上面已经把关键的代码都贴出来了,其实最重要的 地方是按流布局去显示收件人,原生的是没有限制显示行数,也没有处理多行后的滑动轮,头疼的地方是去计算显示高度并动态设置scrollview的高地。坑都已经踩过了,但是这个代码的效率不是很高,有待继续完善,或者有什么好的建议也可以给我提。

时间: 2024-10-10 21:45:18

一个类似MIUI中原生短信编辑功能的相关文章

[Android]手机短信验证功能

介绍 短信验证功能大家都很熟悉了.在很多地方都能见到,注册新用户或者短息验证支付等.短信验证利用短信验证码来注册会员,大大降低了非法注册,很大程度上提高了用户账户的安全性. 目前市面上已经有了很多提供短信验证的服务商,有收费的,也有免费的.如果是个人的开发者,用免费的是最划算的了!下面我就介绍一个免费的短信验证平台---Mob.com Mob平台提供的短信验证功能可以实现快速的验证和匹配通讯录好友功能,并且能为每个app每天提供10000条免费短信验证.很多开发者都想在自己的app中集成短信验证

iOS中发送短信/发送邮件的实现 韩俊强的博客

需要引入框架: MessageUI.framework 布局如下: 短信和邮件: #import "ViewController.h" #import <MessageUI/MessageUI.h> @interface ViewController ()<MFMessageComposeViewControllerDelegate,MFMailComposeViewControllerDelegate>//遵循协议 @end @implementation V

嵌入式设备中使用短信服务

全球移动通信系统GSM是我国范围最广.通信能力最强的移动通信服务,其中的短消息功能以其方便.可靠和价廉得到了广泛应用.如果要在嵌入式设备中使用短信功能,要利用GSM模块实现短信的收发. 短信收发原理 短信服务业务SMS(Short Message Service)是GSM系统提供给用户的一种数字业务,它与语音传输及传真一样,同为GSM通讯网络提供的主要电信业务.系统结构图如下: 短消息模块的接入方式支持两种模式:基于AT指令的TEXT模式和基于AT指令的PDU模式.文本模式只支持7-bit编码,

iOS中调用短信、电话、邮件、Safari浏览器API

//调用短信 - (void)callMsg:(id)sender { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms://135587"]]; } //调用电话 - (void)callTel:(id)sender { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel://135587"

分期花呗 真有这么一个公司,坑人的短信

分期花呗 真有这么一个公司,坑人的短信 诈骗的最高境界就是:制造证据链. 证明对方是诈骗. 得警惕千万别小看诈骗短信, 有些短信你不理,就是一埋下了一个邪恶的种子. 收到 分期花呗 两条短信,第一条是注册成功,提取金额, 第二条是交易成功,金额减少.这就形成了证据链. 人家已经尽到了告知义务.然后你再查询你得支付宝,没问题啊. 但是这个 分期花呗 和支付宝没有任何关系 . pc 搜索链接 一个是空白,一个指向微博.都是错误指向. 最后经过截取空白链接,才跳转到了真正入口. 但是依然无法测试,你是

potato显示一直连接中 potato短信出错怎么办

potato显示一直连接中 potato短信出错怎么办 请关注微信公众号工三 potato苹果下载不了怎么办 如何下载苹果版potato软件 potato苹果手机版怎么下载,只需要关注微信公众号;工三 就可以轻松解决potato下载安装登陆等问题, potato苹果版商店搜不到了,下载不了,potato安卓版显示一直连接中,potato短信出错发送失败是什么原因, potato电脑版怎么上不去,potato网络激活码怎么获取,请关注微信公众号:工三 potato苹果下载方法有很多,potato是

[HTML] 微信开发之移动手机WEB页面(HTML5)Javascript实现一键拨号及短信发送功能

在做一个微信的微网站中的一个便民服务电话功能的应用,用到移动web页面中列出的电话号码,点击需要实现调用通讯录,网页一键拨号的拨打电话功能. 如果需要在移动浏览器中实现拨打电话,发送email,美国服务器,调用sns等功能,移动手机WEB页面(HTML5)Javascript提供的接口是一个好办法. 采用url链接的方式,实现在Safari ios,香港服务器,Android 浏览器,webos 浏览器,塞班浏览器,IE,Operamini等主流浏览器,进行拨打电话功能. 1.最常用WEB页面J

【SSH网上商城项目实战26】完成订单支付后的短信发送功能

上一节我们使用了Java mail完成了给买家发送邮件的功能,还遗留一个功能,就是给买家发送短信,告诉他订单已经生成之类的.这一节主要介绍一下如何在用户支付完成后自动给用户发送短信. 1.申请短信发送功能 很明显,我们自己不能发短信,也没有做短信的功能,所以我们需要去借助第三方的运营商.但是像中国移动,联通,电信这种不会直接给我们提供短信服务,而是先把短信服务提供给一些运营商,然后这些运营商再提供给我们.  有一个运营商叫“中国网建sms短信通”(官网地址).这个运营商可以给我们提供短信的服务,

PHP 短信验证码功能

现在网站在建设网站时为了保证用户信息的真实性,往往会选择发短信给用户手机发验证码信息,只有通过验证的用户才可以注册,这样既保证了用户的联系信息资料的100%的准确性,也为用户提供了一种最方便快捷的注册方式 . 那么我们今天就来说一下 短信发送原理, 如下图 工作开发流程: 第一.实现php手机短信验证功能的基本思路 1.要找到短信服务提供商,接入短信服务 2.在网站信息提交页面请求发送信息 3.服务器向短信服务提供商通信,提交发送请求 4.短信服务提供商通过运营商将信息发送到用户的手机中第二 :