1. 程式人生 > >Android:表情選擇控制元件

Android:表情選擇控制元件

自定義表情控制元件:

public class FaceView extends LinearLayout {
    private Context mContext;
    private OnItemClickListener mListener;
    private int mCurrentPageNumber;


    public int getCurrentPageNumber() {
        return mCurrentPageNumber;
    }

    public CustomGridview(Context context) {
        super(context);
        this.mContext = context;
        init(context);
    }


    public CustomGridview(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        init(context);
    }

    private void init(Context context) {
        setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
        setOrientation(LinearLayout.VERTICAL);
        View customgridview = LayoutInflater.from(context).inflate(R.layout.face, null, false);
        addView(customgridview);
    }

    private List<Map<String, Object>> getFaceData(int n) {
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        Map<String, Object> map = null;
        int[] arr = FaceUtil.getFaceUtil().faceIds;
        for(int i = n*28; arr.length > i && i< n*28 + 28; i++){
            map = new HashMap<String, Object>();
            map.put("face", arr[i]);
            list.add(map);
        }
        return list;
    }


    private List<Integer> getFaceViewIds(){
        List<Integer> viewIds = new ArrayList<Integer>();
        viewIds.add(R.id.face_item_iv);
        return viewIds;
    }
    
    private GridView initFaceUI(Context context, int len){
        GridView gridView = new GridView(context);
        gridView.setAdapter(new MainBaseAdapter(context, R.layout.face_item, getFaceViewIds(), getFaceData(len)));
        gridView.setNumColumns(7);
        gridView.setColumnWidth(80);
        gridView.setHorizontalSpacing(1);
        gridView.setVerticalSpacing(1);
        gridView.setGravity(Gravity.CENTER);
        gridView.setBackgroundColor(Color.parseColor("#f2f2f2"));
        gridView.setOnItemClickListener(mListener);
        return gridView;
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.mListener = listener;
        //表情介面左右拖動控制元件類
        ScrollViewGroup scrollViewGroup = (ScrollViewGroup) findViewById(R.id.scrollViewGroup);
        //加入2頁表情
        for (int i = 0; i < 2; i++) {
            scrollViewGroup.addView(initFaceUI(mContext, i));
        }
        scrollViewGroup.setCurrentPageNumber(0);
        //當拖動表情介面時更新底部圖示為選中狀態
        scrollViewGroup.setOnScrollListener(new OnScrollListener() {
            @Override
            public void onSroll(int currentPage) {
                mCurrentPageNumber = currentPage;
            }
        });
        //底部圖示切換類
        PageControlView pageControlView = (PageControlView) findViewById(R.id.pageControlView);
        pageControlView.bindScrollViewGroup(scrollViewGroup);
    }

}

face.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="215dip"
    android:background="#FFFFFF"
    android:orientation="vertical" >


    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="215dip"
        android:layout_weight="1" >


        <com.g.im.widget.ScrollViewGroup
            android:id="@+id/face_scrollv"
            android:layout_width="fill_parent"
            android:layout_height="175dip" />


        <com.g.im.widget.PageControlView
            android:id="@+id/face_controlv"
            android:layout_width="fill_parent"
            android:layout_height="40dip"
            android:layout_marginTop="175dip"
            android:background="#8f00000f"
            android:gravity="center" />
    </RelativeLayout>


</LinearLayout>

face_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:background="@color/white"
    android:gravity="center"
    android:orientation="vertical"
    android:paddingBottom="10dip"
    android:paddingTop="10dip" >


    <ImageView
        android:id="@+id/face_item_iv"
        android:layout_width="23dip"
        android:layout_height="23dip"
        android:gravity="center" />


</LinearLayout>

顯示錶情頁的類,可左右拖動:
public class ScrollViewGroup extends ViewGroup {
    private Scroller mScroller;
    private VelocityTracker mVelocityTracker;
    private static final int TOUCH_STATE_REST = 0;
    private static final int TOUCH_STATE_SCROLLING = 1;
    private static final int SNAP_VELOCITY = 600;
    private int mTouchState = TOUCH_STATE_REST;
    private int mTouchSlop;
    private float mLastMotionX;

    private int mCurrentPageNumber;  //頁碼
    /**指定表情介面預設顯示為指定頁,供外部呼叫**/
    public void setCurrentPageNumber(int currentPageNumber) {
        this.mCurrentPageNumber = currentPageNumber;
    }
    
    /**取得當前頁碼**/
    public int getCurrentPageNumber() {
        return mCurrentPageNumber;
    }


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


    public ScrollViewGroup(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mScroller = new Scroller(context);
        mCurrentPageNumber = 0;
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    }

    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {   
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        final int width = MeasureSpec.getSize(widthMeasureSpec);
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        if (widthMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("ScrollLayout only canmCurScreen run at EXACTLY mode!");
        }
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("ScrollLayout only can run at EXACTLY mode!");
        }
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }
        scrollTo(mCurrentPageNumber * width, 0);
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            int childLeft = 0;
            final int childCount = getChildCount();
            //設定子view的位置為按順德序橫向排列
            for (int i = 0; i < childCount; i++) {
                final View childView = getChildAt(i);
                if (childView.getVisibility() != View.GONE) {
                    final int childWidth = childView.getMeasuredWidth();
                    childView.layout(childLeft, 0, childLeft + childWidth, childView.getMeasuredHeight());
                    childLeft += childWidth;
                }
            }
        }
    }

    @Override
    public void computeScroll() {
        //滾動動作已經結束進執行
        if (mScroller.computeScrollOffset()) {
            //設定檢視的滾動位置。會觸發 onScrollChanged(int, int, int, int) 事件,並使檢視失效重繪。
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            //更新檢視
            postInvalidate();
        }
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);
        final int action = event.getAction();
        final float x = event.getX();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                mLastMotionX = x;
                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = (int) (mLastMotionX - x);
                mLastMotionX = x;
                scrollBy(deltaX, 0);
                break;
            case MotionEvent.ACTION_UP:
                final VelocityTracker velocityTracker = mVelocityTracker;
                //初始化速率的單位,1000毫秒內運動了多少個畫素
                velocityTracker.computeCurrentVelocity(1000);
                int velocityX = (int) velocityTracker.getXVelocity();
                //判斷橫向的速率
                if (velocityX > SNAP_VELOCITY && mCurrentPageNumber > 0) {//右移,顯示上個頁面
                    scrollToScreen(mCurrentPageNumber - 1);
                } else if (velocityX < -SNAP_VELOCITY && mCurrentPageNumber < getChildCount() - 1) {//左移,顯示下個頁面
                    scrollToScreen(mCurrentPageNumber + 1);
                } else {//根據滾動位置回彈
                    snapToDestination();
                }
                //釋放
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                mTouchState = TOUCH_STATE_REST;
                break;
            case MotionEvent.ACTION_CANCEL:
                mTouchState = TOUCH_STATE_REST;
                break;
        }
        return true;
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
            return true;
        }
        final float x = ev.getX();
        switch (action) {
            case MotionEvent.ACTION_MOVE:
                final int xDiff = (int) Math.abs(mLastMotionX - x);
                if (xDiff > mTouchSlop) {
                    mTouchState = TOUCH_STATE_SCROLLING;
                }
                break;
            case MotionEvent.ACTION_DOWN:
                mLastMotionX = x;
                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mTouchState = TOUCH_STATE_REST;
                break;
        }
        return mTouchState != TOUCH_STATE_REST;
    }

    /**
    * 根據當前x座標位置確定切換到第幾屏
    */
    public void snapToDestination() {
        final int screenWidth = getWidth();
        final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
        scrollToScreen(destScreen);
    }
    
    /**
    * 切換到指定屏
    * 
    * @param whichScreen  切換頁碼
    */
    public void scrollToScreen(int whichScreen) {
        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
        if (getScrollX() != (whichScreen * getWidth())) {
            final int delta = whichScreen * getWidth() - getScrollX();
            mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
            mCurrentPageNumber = whichScreen;
            invalidate();
            /** 切換屏後回撥,更新底部圖示 **/
            if (onScreenChangeListener != null) {
                onScreenChangeListener.onScreenChange(mCurrentPageNumber);
            }
            if (onScrollListener != null) {
                onScrollListener.onSroll(mCurrentPageNumber);
            }
        }
    }

    /**供底部圖示切換類使用**/
    private OnScreenChangeListener onScreenChangeListener;
        public void setOnScreenChangeListener(OnScreenChangeListener onScreenChangeListener) {
            this.onScreenChangeListener = onScreenChangeListener;
    }
    
    public interface OnScreenChangeListener { 
                void onScreenChange(int currentIndex); 
       }  

    /**供底外部GridView獲取當前頁碼使用**/ 
    private OnScrollListener onScrollListener;
    public void setOnScrollListener(OnScrollListener onScrollListener) { this.onScrollListener = onScrollListener; } public interface OnScrollListener { 
         void onSroll(int currentPage); 
    }
}

切換控制元件底部圖示的類:
public class PageControlView extends LinearLayout {
    private Context mContext;
    private int mPageNumber; // 頁數
    private ScrollViewGroup mScrollViewGroup;


    public PageControlView(Context context) {
        super(context);
        init(context);
    }


    public PageControlView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }


    private void init(Context context) {
        this.mContext = context;
    }


    /**
     * 據據表情頁更新底部對應圖示為選中狀態
     * 
     * @param scrollViewGroup 仿lauch可左右拖動介面的物件
     */
    public void bindScrollViewGroup(ScrollViewGroup scrollViewGroup) {
        this.mScrollViewGroup = scrollViewGroup;
        this.mPageNumber = scrollViewGroup.getChildCount();
        generatePageControl(scrollViewGroup.getCurrentPageNumber());
        /** 當拖動表情介面時更新底部圖示為選中狀態 **/
        scrollViewGroup.setOnScreenChangeListener(new OnScreenChangeListener() {
            @Override
            public void onScreenChange(int currentPage) {
                generatePageControl(currentPage);
            }
        });
    }


    /**
     * 跟據當前頁碼將當前頁的圖示換成選中狀態樣式的圖片
     * 
     * @param currentIndex 當前頁碼
     */
    private void generatePageControl(int currentPage) {
        this.removeAllViews();
        for (int i = 0; i < mPageNumber; i++) {
            ImageView imageView = new ImageView(mContext);
            imageView.setPadding(5, 5, 5, 5);
            if (currentPage == i) {
                imageView.setImageResource(R.drawable.page_indicator_focused);
            } else {
                imageView.setImageResource(R.drawable.page_indicator_unfocused);
            }
            this.addView(imageView);
        }
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean temp = event.getX() > getWidth() / 2;
        if (temp && mScrollViewGroup.getCurrentPageNumber() < 1) {// 下一頁
            mScrollViewGroup.scrollToScreen(mScrollViewGroup.getCurrentPageNumber() + 1);
        } else if (!temp && mScrollViewGroup.getCurrentPageNumber() > 0) {// 上一頁
            mScrollViewGroup.scrollToScreen(mScrollViewGroup.getCurrentPageNumber() - 1);
        }
        return super.onTouchEvent(event);
    }


}
在聊天介面中加入自定義表情佈局:
<com.g.im.widget.FaceView
    android:id="@+id/talk_face_gridview"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@color/face_gridview_bg"
    android:columnWidth="80dip"
    android:gravity="center"
    android:horizontalSpacing="1dip"
    android:listSelector="@color/transparent"
    android:numColumns="7"
    android:verticalSpacing="1dip" />