1. 程式人生 > >仿微信朋友圈【九宮格的實現】

仿微信朋友圈【九宮格的實現】

最近有個想法,想用環信的sdk去做個社交類的小demo玩。在此之前,先來模仿下微信的朋友圈九宮格效果。同時也相容了QQ的做法,如果資料集大於九張時,就在最後一張圖片上顯示一層遮罩效果,並顯示剩餘圖片的數量。之後的計劃是仿微信的朋友圈評論、回覆這方面的效果,在實際開發中還是比較實用的。

老規矩,先來張效果圖(錄製的圖片太大滿足不了神經的CSDN上傳要求,壓縮又不清晰,所以還是放幾張靜態圖吧)
這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

需求分析

  1. 單張的情況,我們需要考慮圖片的寬度與我們自定義九宮格控制元件的寬度,如果圖片的寬度大於控制元件的寬度,那麼我們就用控制元件的寬度作為圖片的寬度
  2. 2 X 2的情況,微信朋友圈對於4張的圖片,採用的是2 X 2的佈局方式
  3. 當圖片集大於9張時,我們需要在最後一張圖片上顯示一層遮罩,並顯示出剩餘的圖片數量
  4. 最後一點需要特別注意,就是關於我們自定義控制元件的複用。比如說我們滑出螢幕的一個item的佈局是7張圖片,這時滑進螢幕的item是6張圖片的佈局,難道我們還需要再重新new出來六個imageview?當然不是,我們完全可以複用滑出螢幕的那個佈局,同時移除一個imageview即可。反之如果當前滑入的item是9張,那麼複用後就只需要再new出來兩個imageview控制元件即可。其實是跟ListView的複用機制思想差不多。

根據上面的分析,實現起來應該相對有些思路了。下面就開啟自定義模式了

自定義屬性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="NineGridView">
        <attr name="nine_gv_spacing" format="dimension"/>
        <attr name="nine_maxImageNum" format="integer"/>
        <attr name="nine_single_image" format="dimension"
/>
</declare-styleable> </resources>

在我們自定義類的構造方法中去獲取我們的自定義屬性

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

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

    public NineGridView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //單位轉換
        mNineGridViewSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mNineGridViewSpacing, context.getResources().getDisplayMetrics());
        mSingleImageSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mSingleImageSize, context.getResources().getDisplayMetrics());
        //獲取自定義屬性
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NineGridView, defStyleAttr, 0);
        int count = typedArray.getIndexCount();
        for (int i=0; i<count; i++){
            int attr = typedArray.getIndex(i);
            switch (attr){
                case R.styleable.NineGridView_nine_gv_spacing:
                    mNineGridViewSpacing = (int) typedArray.getDimension(attr, mNineGridViewSpacing);
                    break;
                case R.styleable.NineGridView_nine_maxImageNum:
                    mMaxImageNum = typedArray.getInt(attr, mMaxImageNum);
                    break;
                case R.styleable.NineGridView_nine_single_image:
                    mSingleImageSize = typedArray.getDimensionPixelSize(attr, mSingleImageSize);
                    break;
            }
        }
        typedArray.recycle();
    }

測量 onMeasure

測量我們控制元件的寬高等,這裡根據上面的分析可知我們需要對單張圖片以及非單張圖片進行判斷。如果是多張圖片的話,我們需要根據行、列個數以及每行每列之間的間距值來算出最終的寬、高

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize;

        int totalWidth = widthSize - getPaddingLeft() - getPaddingRight();
        if(imagesDatasList != null && imagesDatasList.size() > 0){
            if(imagesDatasList.size() == 1){
                //說明是單張圖片
                mWidth = mSingleImageSize > totalWidth ? (int)(totalWidth * 0.8) : mSingleImageSize;
                mHeight = mWidth;
                //進一步根據高度來調整顯示,控制最大顯示範圍
                if(mHeight > mSingleImageSize){
                    float ratio = mSingleImageSize * 1.0f / mHeight;
                    mWidth = (int) (mWidth * ratio);
                    mHeight = mSingleImageSize;
                }
            }else{
                //說明不止一張
                mWidth = mHeight = (totalWidth - mNineGridViewSpacing*(columnCount - 1)) / columnCount;
            }
            widthSize = mWidth * columnCount + mNineGridViewSpacing * (columnCount - 1) + getPaddingLeft() + getPaddingRight();
            heightSize = mHeight * rowCount + mNineGridViewSpacing * (rowCount - 1) + getPaddingTop() + getPaddingBottom();
            setMeasuredDimension(widthSize, heightSize);
        }else{
            heightSize = widthSize;
            setMeasuredDimension(widthSize, heightSize);
        }
    }

其實,實際開發中可能伺服器返回的還有圖片的寬高比例,那麼我們可以根據這個寬高比例還算出圖片的高度等等,具體情況根據業務來定。

確定位置 onLayout

既然是自定義ViewGroup,那麼onLayout()方法肯定少不了。它是用來確定子view的位置的

@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if(imagesDatasList == null) return;
        int childCount = imagesDatasList.size() > mMaxImageNum ? mMaxImageNum : imagesDatasList.size();
        for(int i = 0; i < childCount; i++){
            ImageView childView = (ImageView) getChildAt(i);
            if(mAdapter != null){
                mAdapter.onDisplayImage(getContext(), childView, imagesDatasList.get(i));//得到圖片陣列中的每一張圖片
            }
            //通過此方式來確定寬高是否累加、換行,一併判斷了
            int columnNum = i % columnCount;
            int rowNum = i / columnCount;
            left = (mWidth + mNineGridViewSpacing ) * columnNum + getPaddingLeft();//根據i來決定left, i=0 left=getPaddingLeft   i=1表示第二個childView的left=第一個child的寬+間距+內間距
            top = (mHeight + mNineGridViewSpacing) * rowNum + getPaddingTop();
            right = left + mWidth;
            bottom = top + mHeight;

            childView.layout(left, top, right, bottom);
        }
    }

接下來就是我們的adapter跟這個自定義控制元件的互動了

public void setData(List<String> mDataLists){
        //有無資料決定著九宮格控制元件的顯示與隱藏
        if(mDataLists == null || mDataLists.isEmpty()){
            this.setVisibility(View.GONE);
            return;
        }else{
            this.setVisibility(View.VISIBLE);
        }
        //獲取圖片數量,圖片的數量有可能大於規定的最大數量9張
        int newImgCount = mDataLists.size() > mMaxImageNum ? mMaxImageNum : mDataLists.size();
        //給rowCount、columnCount行列賦值。對圖片的分佈特殊處理,比如 四張  2 X 2 分佈
        setRowAndColumn(newImgCount);
        //複用
        if(imagesDatasList == null){
            for(int i = 0; i < newImgCount; i++){
                ImageView iv = imageViewHolder(i);
                if(iv == null) return;
                addView(iv, generateDefaultLayoutParams());
            }
        } else {
            int oldImgCount = imagesDatasList.size() > mMaxImageNum ? mMaxImageNum : imagesDatasList.size();//原來的圖片資料數量
            if(newImgCount < oldImgCount){
                //說明可以複用原來的imageview   移除後面多餘的view(imageview)佈局
                removeViews(newImgCount,oldImgCount - newImgCount);
            }else if(newImgCount > oldImgCount){
                //說明需要再新new幾個imageview提供多餘的資料使用
                for(int i=oldImgCount; i < newImgCount; i++){
                    ImageView iv = imageViewHolder(i);
                    if(iv == null) return;
                    addView(iv, generateDefaultLayoutParams());//將imageview新增到預設寬高的佈局中
                }
            }
        }
       //如果是最後一張,並且圖片的資料集總數大於九張,那麼就在最後一張圖片上展示還剩圖片的數量
        if (mDataLists.size() > mMaxImageNum){
            View child = getChildAt(mMaxImageNum - 1);//九宮格的最後一張圖片
            if(child instanceof MyGridViewItemImageView){
                MyGridViewItemImageView imageView = (MyGridViewItemImageView) child;
                imageView.setImagesCount(mDataLists.size());
            }
        }
        imagesDatasList = mDataLists;
        //當view佈局內容發生改變後呼叫此方法會重新走onMeasure()和onLayout()方法,重新調整佈局
        //requestLayout(); //因為addViews()方法內部已經有requestLayout()了
    }

我們需要在展示資料的adapter中去呼叫此方法。這裡為了呼叫的簡潔,我們額外定義了一個抽象類。

public abstract class NineGridViewAdapter {

    protected abstract void onDisplayImage(Context context, ImageView iv, String url);

    protected void onItemImageClick(Context context, ImageView iv, int position, List<String> list){

    }

    protected ImageView generateImageView(Context context){
        MyGridViewItemImageView imageView = new MyGridViewItemImageView(context);//設定圖片的點選背景顏色變化效果
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        return imageView;
    }
}

這裡需要特別說明的是generateImageView()方法,這裡面我們new出來我們的九宮格中的一張張圖片。同時,還就點選圖片變暗的點選效果以及超過9張後的效果處理。下面就具體看看

/**
 * 設定圖片點選時有個背景色,鬆手後移除背景色   類似XML檔案設定selector效果
 */

public class MyGridViewItemImageView extends ImageView{

    private int textColor = Color.parseColor("#FFFFFF");
    private int textSize;
    private int imageViewBg = 0x88000000;
    private int imagesCount;//總的資料集
    private String textDesc;//要繪製的文字

    private Paint paint;

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

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

    public MyGridViewItemImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 32, context.getResources().getDisplayMetrics());

        //初始化畫筆
        iniPaint();
    }

    private void iniPaint() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setColor(textColor);
        paint.setTextSize(textSize);
        paint.setTextAlign(Paint.Align.CENTER);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(imagesCount > 9){
            canvas.drawColor(imageViewBg);//背景顏色
            Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
            float baseLine = getHeight() / 2 - (fontMetrics.bottom + fontMetrics.top) / 2;
            canvas.drawText(textDesc, getWidth() / 2, baseLine, paint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Drawable drawable = getDrawable();
                if(drawable != null){
                    //drawable.mutate().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
                    drawable.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
                    ViewCompat.postInvalidateOnAnimation(this);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                Drawable drawableUp = getDrawable();
                if(drawableUp != null){
                    //drawableUp.mutate().clearColorFilter();
                    drawableUp.clearColorFilter();
                    ViewCompat.postInvalidateOnAnimation(this);
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        //將drawable物件置空
        setImageDrawable(null);
    }

    public int getImagesCount() {
        return imagesCount;
    }

    public void setImagesCount(int imagesCount) {
        this.imagesCount = imagesCount;
        textDesc = "+"+(imagesCount - 9);
        invalidate();
    }
}

需要特別說明的一點是drawable.mutate().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);這個方法。根據jeasonlzy大神的解釋是,如果這樣寫的話在部分機型上會出問題,所以他給出了一個解決方案。由於現有測試機種類有限,目前還沒有出現他說的這種問題。不管了,先給出兩種實現方式。

接下來,再來看看我們的adapter是如何呼叫互動的

/**
 *  展示資料的介面卡 adapter
 */

public class RecyclerViewDatasAdapter extends RecyclerView.Adapter<RecyclerViewDatasAdapter.ImageViewHolder>{

    private Context context;
    private List<ImagesBean> lists;
    private LayoutInflater inflater;

    public RecyclerViewDatasAdapter(Context context, List<ImagesBean> lists) {
        this.context = context;
        this.lists = lists;
        inflater = LayoutInflater.from(context);
    }

    @Override
    public ImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ImageViewHolder(inflater.inflate(R.layout.item_layout, parent, false));
    }

    @Override
    public void onBindViewHolder(ImageViewHolder holder, int position) {
        holder.iv.setImageResource(R.mipmap.ic_launcher);
        holder.tvName.setText(lists.get(position).getName());
        holder.tvDesc.setText(lists.get(position).getDesc());
        holder.nineGridView.setData(lists.get(position).getImgsUrl());//將圖片集合傳到我們的自定義九宮格控制元件中
    }

    @Override
    public int getItemCount() {
        return null != lists ? lists.size() : 0;
    }

    public class ImageViewHolder extends RecyclerView.ViewHolder{

        private ImageView iv;
        private TextView tvName;
        private TextView tvDesc;
        private NineGridView nineGridView;

        private NineGridViewAdapter nineGridViewAdapter = new NineGridViewAdapter() {
            @Override
            protected void onDisplayImage(Context context, ImageView iv, String url) {
                //Glide.with(context).load(url).into(iv);
                Picasso.with(context).load(url).into(iv);
            }

            @Override
            protected ImageView generateImageView(Context context) {
                return super.generateImageView(context);
            }

            @Override
            protected void onItemImageClick(Context context, ImageView iv, int position, List<String> list) {
                Toast.makeText(context, "你點選了 position = " + position, Toast.LENGTH_SHORT).show();
                //super.onItemImageClick(context, iv, position, list);
            }
        };

        public ImageViewHolder(View itemView) {
            super(itemView);
            iv = (ImageView) itemView.findViewById(R.id.iv);
            tvName = (TextView) itemView.findViewById(R.id.tv_name);
            tvDesc = (TextView) itemView.findViewById(R.id.tv_desc);
            nineGridView = (NineGridView) itemView.findViewById(R.id.nineGridView);
            nineGridView.setDataAdapter(nineGridViewAdapter);
        }
    }
}

這裡需要特別說明一下,大家可以看到這裡我採用的是Glide載入圖片,在測試中發現當圖片大於九張時會出現圖片部分被放大(也就是所謂的變形),開始我以為是自定義控制元件哪寫的有問題,但是經過反覆測試,發現是Glide載入的問題。按照網上說的方式,比如關掉載入動畫等,發現並不能解決。Glide的原始碼著實太複雜,所以目前並不能很好的解決這個問題。以後有時間再繼續研究吧,目前我換用了其它的圖片載入框架就沒問題了。

順便把我們的實體類也貼出來吧

/**
 * 實體類
 */

public class ImagesBean implements Serializable{
    private static final long serialVersionUID = 370114387259948705L;

    private int imgs;
    private String name;
    private String desc;
    private ArrayList<String> imgsUrl;//圖片陣列集合

    public ImagesBean(String name, String desc, ArrayList<String> imgsUrl) {
        this.name = name;
        this.desc = desc;
        this.imgsUrl = imgsUrl;
    }

    public int getImgs() {
        return imgs;
    }

    public void setImgs(int imgs) {
        this.imgs = imgs;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public ArrayList<String> getImgsUrl() {
        return imgsUrl;
    }

    public void setImgsUrl(ArrayList<String> imgsUrl) {
        this.imgsUrl = imgsUrl;
    }

}

最後是我們的MainActivity

 public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private RecyclerViewDatasAdapter adapter;
    private List<ImagesBean> mDatas;
    private String[] imgsUrl = {
            "https://pic4.zhimg.com/02685b7a5f2d8cbf74e1fd1ae61d563b_xll.jpg",
            "https://pic4.zhimg.com/fc04224598878080115ba387846eabc3_xll.jpg",
            "https://pic3.zhimg.com/d1750bd47b514ad62af9497bbe5bb17e_xll.jpg",
            "https://pic4.zhimg.com/da52c865cb6a472c3624a78490d9a3b7_xll.jpg",
            "https://pic3.zhimg.com/0c149770fc2e16f4a89e6fc479272946_xll.jpg",
            "https://pic1.zhimg.com/76903410e4831571e19a10f39717988c_xll.png",
            "https://pic3.zhimg.com/33c6cf59163b3f17ca0c091a5c0d9272_xll.jpg",
            "https://pic4.zhimg.com/02685b7a5f2d8cbf74e1fd1ae61d563b_xll.jpg",
            "https://pic4.zhimg.com/fc04224598878080115ba387846eabc3_xll.jpg",
            "https://pic3.zhimg.com/d1750bd47b514ad62af9497bbe5bb17e_xll.jpg",
            "https://pic4.zhimg.com/da52c865cb6a472c3624a78490d9a3b7_xll.jpg",
            "https://pic3.zhimg.com/0c149770fc2e16f4a89e6fc479272946_xll.jpg",
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        //測試資料
        mDatas = new ArrayList<>();
        for(int i=0; i < 12; i++){
            ArrayList<String> imgs = new ArrayList<>();
            imgs.addAll(Arrays.asList(imgsUrl).subList(0, i % 12 + 1));
            ImagesBean bean = new ImagesBean("我是bean", "測試九宮格圖片,只是測試demo,只是測試demo",imgs);
            mDatas.add(bean);
        }
        adapter = new RecyclerViewDatasAdapter(this, mDatas);
        recyclerView.setAdapter(adapter);
    }
}

最後,非常感謝laobie大牛,此專案就是參考他的專案。如果大家覺得還有什麼問題的話,歡迎留言交流。

相關推薦

仿朋友九宮實現

最近有個想法,想用環信的sdk去做個社交類的小demo玩。在此之前,先來模仿下微信的朋友圈九宮格效果。同時也相容了QQ的做法,如果資料集大於九張時,就在最後一張圖片上顯示一層遮罩效果,並顯示剩餘圖片的數量。之後的計劃是仿微信的朋友圈評論、回覆這方面的效果,在實際

安卓專案實戰之仿朋友九宮自定義控制元件

效果圖 圖片展示形式 1、當只有1張圖時,可以自己定製圖片寬高,也可以使用預設九宮格的寬高; 2、當只有4張圖時,以2*2的方式顯示; 3、除以上兩種情況下,都是按照3列方式顯示,但這時有一些細節: a、如果只有9張圖,當然是以3*3的方式顯示;

Android仿朋友九宮圖片展示自定義控制元件,支援縮放動畫~

一直對微信朋友圈九宮格圖片顯示控制元件比較好奇,找到一篇介紹相關騷操作的部落格 部落格雖好但是不夠完美,缺少點選圖片預覽頁面和縮放動畫,作為一個不斷追求完美主義的人,我想把這個控制元件結合到專案中而不是單純作為一個控制元件。 下面是我的實現效果圖: (

Android 修改源碼自定義SwipeRefreshLayout樣式——高仿朋友下拉刷新

樣式 post and 微信 修改 size roi 自定義 details 修改源碼自定義SwipeRefreshLayout樣式——高仿微信朋友圈下拉刷新Android 修改源碼自定義SwipeRefreshLayout樣式——高仿微信朋友圈下拉

Luban(魯班) —— Android圖片壓縮工具,仿朋友壓縮策略。

專案描述 目前做App開發總繞不開圖片這個元素。但是隨著手機拍照解析度的提升,圖片的壓縮成為一個很重要的問題。單純對圖片進行裁切,壓縮已經有很多文章介紹。但是裁切成多少,壓縮成多少卻很難控制好,裁切過頭圖片太小,質量壓縮過頭則顯示效果太差。 於是自然想到App巨頭“微信”會是怎麼處理,

仿朋友圖冊瀏覽及其擴充套件

書到用時方恨少 我很笨,歷時3天半,才把這個圖冊瀏覽的外掛搞定了。很感謝前輩寫完的外掛,不然我弄一個月也弄不出來。 功能點: 1、PC端的圖片要類似ppt,有放大但不失真功能(放大有了,圖片太小肯定會失真),可點選圖片和鍵盤控制翻頁; 2、移動端支援縮放、滑動,左右控制翻頁;

Android仿朋友高清圖檢視控制元件可縮放、雙擊、移動

該庫支援和包含的功能: 1.圖片支援手勢操作, 可縮放、雙擊、移動 2.圖片載入時的進度條, 支援自定義 該庫的效果圖如下: 本地相簿圖片效果圖: 點選預覽大圖的效果 本想直接新增gradle依賴庫,不巧的是,之前版本已作廢,待現在版本穩定後,

仿朋友檢視圖片下拽返回。整合輕鬆,效果超讚~

ImageWatcher 大圖檢視,它能夠 動畫順暢切換到檢視狀態,同樣動畫順暢退出檢視介面 左右滑動多圖檢視 仿微信下拽退出     對比之前1.0.3, 修復-寬高計算錯誤導致起

仿朋友圖片點選放大效果

這兩天寫了個小專案,其中用到了點選一組圖片中的一張可以檢視它的放大版,用手向左滑動能看到下一張的這組圖片中的第二張的放大版。。。依次類推,單擊放大版的圖片實現關閉效果,對於js能力不太好的我只能找度娘,然後就發現了photoswiper.js,不得不說真的好用,

仿朋友預覽功能

話不多說,先上圖主要功能就是如圖中所示,點選圖片放大,拖拽圖片縮小到列表中圖片位置處消失。這個功能其實原理是這樣的(我猜):首先點選列表中的圖片跳轉到新的Acticity進行預覽,當然這個Activity必須是透明的;跳轉的時候需要將列表圖片的螢幕位置和寬高傳遞到新Activ

Android仿朋友10s視訊編輯

以前遇到一個需求就是要做一個類似微信朋友圈10s視訊上傳的功能,因此就需要視訊裁剪與壓縮,需要做一個類似微信朋友圈視訊編輯的頁面,就需要將視訊按照時間一秒一秒解碼成一幅幅的圖片,讓使用者滑動選擇。之前在網上找到了一個類似專案用的 MediaMetadat

Android 仿朋友 Textview 全文 收起展示

先看效果圖:適用listview recycleview 滑動流暢,無BUG上程式碼,直接寫在adapter裡private final int MAX_LINE_COUNT = 3;//最大顯示行數

自定義控制元件 仿朋友文字展開全文功能

自定義TextView仿微信朋友圈文字資訊,展開全文功能 程式碼及註釋如下: 首先寫一個xml檔案 showmore.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout x

仿朋友回覆資訊-點選空白處軟鍵盤以及回覆佈局消失

開啟微信朋友圈,點選回覆按鈕,會立馬跳出一個回覆欄和一個軟鍵盤,點選其他空白處這2個又消失了。這種功能我們怎麼實現呢? 類似下面這種功能,由於用的是夜神模擬器,沒有軟鍵盤,大家可以自行腦補,或者去自己的朋友圈看看效果。 思路是這樣的:點選這個回覆狀態列以外的其他區域,讓這

Android實現仿朋友釋出動態(拍照、相簿選擇、照片壓縮、顯示、儲存、縮圖、點選縮圖刪除對應檔案等)附原始碼

         原創作品,轉載請註明出處:http://blog.csdn.net/zhang3776813/article/details/52092591 最近專案需求中要用到類似微信朋友圈釋出動態選擇圖片的UI效果,研究了一下,特來分享成果,多的不說來看程式碼。

iOS:仿朋友程式碼

仿WeChat朋友圈的介面效果,程式碼整潔易讀,靜態頁面,無網路互動。裡面的功能並沒有全部完善,可以根據自己的需求去補充一下。 程式碼結構 每個類檔案中都有詳細的備註和註釋,模組劃分和流程都還蠻清晰的,這裡就不贅述了,讀程式碼更直接一些。 檢視結構 Cell的

[Android例項] 自定義控制元件一百行程式碼實現朋友九宮圖片顯示

package com.weixinninegridlayout;import android.content.Context;import android.graphics.Color;import android.graphics.drawable.ColorDrawable;import android

HTML5仿聊天界面、朋友實例

很好 order 分享圖片 ext mount case 天使 inf 仿微信 這幾天使用H5開發了一個仿微信聊天前端界面,尤其微信底部編輯器那塊處理的很好,使用HTML5來開發,雖說功能效果並沒有微信那麽全,但是也相當不錯了,可以發送消息、表情,發送的消息自動回滾定位到底

Android 仿朋友九宮多圖顯示(二)

一個仿微信朋友圈和QQ空間的九宮格圖片展示自定義控制元件效果: 一.介紹: 1、當只有1張圖時,可以自己定製圖片寬高,也可以使用預設九宮格的寬高; 2、當只有4張圖時,以2*2的方式顯示; 3、除以上兩種情況下,都是按照3列方式顯示

學習寫測試用例2如何測試朋友對你的狀態進行點贊

1.是否可以正常點贊和取消; 2.點讚的人是否在可見分組裡; 3.點贊狀態是否能即時更新顯示; 4.點贊狀態,共同好友是否可見; 5.不同手機,系統顯示介面如何; 6.效能檢測,網速快慢對其影響; 7.點贊顯示的是否正確,一行幾個; 8.點贊是否按時間進行排序,頭像對應的是