前言:

微信朋友圈网上有很多开源的例子,本文是自己手写,之前的几篇都是单个功能,感觉很零散,这次放出完整的点赞评论和朋友圈列表功能,大家有兴趣可以看一下

1.定义四种item类型,可以按后台接口自行定义类型:

img

2.CircleAdapter实现代码:

部分核心代码截图:

img

img

3.CircleAdapter完整代码:

/**
 * @作者: njb
 * @时间: 2019/7/25 10:47
 * @描述: 朋友圈适配器
 */
public class CircleAdapter extends BaseMultiItemQuickAdapter<CircleBean.DataBean,BaseViewHolder>{
    private TextView tvAddress, tvDelete;
    private ImageView imageView;
    private ExpandTextView tvContent;
    private ImageWatcher imageWatcher;
    private RequestOptions mRequestOptions;
    private DrawableTransitionOptions mDrawableTransitionOptions;
    private LinearLayout llComment;
    private EditText etComment;
    private int x;
    private int y;
    private PopupWindow mPopupWindow;
    private Click click;
​
    public CircleAdapter(@Nullable List<CircleBean.DataBean> data, ImageWatcher imageWatcher, LinearLayout llComment, EditText etComment, Click click) {
        super(data);
        //文本
        addItemType(Constants.TYPE_TEXT, R.layout.item_text);
        //图片
        addItemType(Constants.TYPE_IMAGE, R.layout.item_image);
        //视频
        addItemType(Constants.TYPE_VIDEO, R.layout.item_video);
        //网页
        addItemType(Constants.TYPE_WEB, R.layout.item_web);
        this.imageWatcher = imageWatcher;
        this.mRequestOptions = new RequestOptions().centerCrop();
        this.mDrawableTransitionOptions = DrawableTransitionOptions.withCrossFade();
        this.llComment = llComment;
        this.etComment = etComment;
        this.click = click;
    }
​
    @Override
    protected void convert(@NonNull BaseViewHolder helper, CircleBean.DataBean item) {
        if(item == null){
            return;
        }
        imageView = helper.getView(R.id.iv_photo);
        tvContent = helper.getView(R.id.tv_content);
        tvAddress = helper.getView(R.id.tv_address);
        tvDelete = helper.getView(R.id.tv_delete);
​
        switch (item.getItemType()) {
            //文本
            case Constants.TYPE_TEXT:
                //用户名
                if (item.getUser_name() != null && !item.getUser_name().equals("")) {
                    helper.setText(R.id.tv_name, item.getUser_name());
                }
                //评论时间
                if (item.getCreateon() != null && !item.getCreateon().equals("")) {
                    helper.setText(R.id.tv_time, item.getCreateon());
                }
                break;
            //图片
            case Constants.TYPE_IMAGE:
                if (item.getUser_name() != null && !item.getUser_name().equals("")) {
                    helper.setText(R.id.tv_name, item.getUser_name());
                }
                if (item.getCreateon() != null && !item.getCreateon().equals("")) {
                    helper.setText(R.id.tv_time, item.getCreateon());
                }
                NineGridView layout = helper.getView(R.id.layout_nine);
                layout.setSingleImageSize(80, 120);
                if (item.getFiles() != null && item.getFiles().size() > 0) {
                    layout.setAdapter(new NineImageAdapter(mContext, mRequestOptions, mDrawableTransitionOptions, item.getFiles()));
                    layout.setOnImageClickListener((position, view) -> {
                        imageWatcher.show((ImageView) view, layout.getImageViews(), item.getImageUriList());
                    });
                }
                break;
            //视频
            case Constants.TYPE_VIDEO:
                if (item.getUser_name() != null && !item.getUser_name().equals("")) {
                    helper.setText(R.id.tv_name, item.getUser_name());
                }
​
                if (item.getCreateon() != null && !item.getCreateon().equals("")) {
                    helper.setText(R.id.tv_time, item.getCreateon());
                }
                ImageView ivVideo = helper.getView(R.id.video_view);
                //视频封面图
                if (item.getFiles() != null && item.getFiles().size() > 0) {
                    GlideUtils.loadImg(mContext, item.getFiles().get(0), ivVideo);
                }
                break;
            //网页
            case Constants.TYPE_WEB:
                ImageView ivUser = helper.getView(R.id.iv_user);
                if (item.getUser_name() != null && !item.getUser_name().equals("")) {
                    helper.setText(R.id.tv_name, item.getUser_name());
                }
​
                if (item.getCreateon() != null && !item.getCreateon().equals("")) {
                    helper.setText(R.id.tv_time, item.getCreateon());
                }
                if (item.getShare_image() != null && !item.getShare_image().equals("")) {
                    GlideUtils.loadImg(mContext, item.getShare_image(), ivUser, R.drawable.ic_launcher_background);
                }
                if (item.getShare_title() != null && !item.getShare_title().equals("")) {
                    helper.setText(R.id.tv_text, item.getShare_title());
                } else {
                    helper.setText(R.id.tv_text, "");
                }
                break;
    }
​
        //用户头像
        if (!TextUtils.isEmpty(item.getHead_img())) {
            GlideUtils.loadImg(mContext, item.getHead_img(), imageView, R.drawable.ic_launcher_background);
        } else {
            imageView.setImageResource(R.drawable.ic_launcher_background);
        }
        //评论内容
        if (TextUtils.isEmpty(item.getContent())) {
            tvContent.setVisibility(View.GONE);
        } else {
            tvContent.setVisibility(View.VISIBLE);
            tvContent.setText(item.getContent());
        }
        if (!TextUtils.isEmpty(item.getPosition()) && !item.getPosition().equals("该位置信息暂无")) {
            tvAddress.setVisibility(View.VISIBLE);
            tvAddress.setText(item.getPosition());
        } else {
            tvAddress.setVisibility(View.GONE);
        }
        tvDelete.setVisibility(View.VISIBLE);
        LinearLayout llLike = helper.getView(R.id.ll_like);
        PraiseListView rvLike = helper.getView(R.id.rv_like);
        CommentsView rvComment = helper.getView(R.id.rv_comment);
        View viewLike = helper.getView(R.id.view_like);
​
        helper.addOnClickListener(R.id.iv_edit);
        helper.addOnClickListener(R.id.tv_like);
        helper.addOnClickListener(R.id.tv_delete);
        helper.addOnClickListener(R.id.iv_user);
        helper.addOnClickListener(R.id.video_view);
        helper.addOnClickListener(R.id.iv_photo);
        helper.addOnClickListener(R.id.tv_name);
        helper.addOnClickListener(R.id.ll_share);
        helper.addOnLongClickListener(R.id.tv_content);
​
        if ((item.getLike_list() != null && item.getLike_list().size() > 0) && (item.getComments_list() != null && item.getComments_list().size() > 0)) {
            viewLike.setVisibility(View.VISIBLE);
        } else {
            viewLike.setVisibility(View.GONE);
        }
        if ((item.getLike_list() != null && item.getLike_list().size() > 0) || (item.getComments_list() != null && item.getComments_list().size() > 0)) {
            llLike.setVisibility(View.VISIBLE);
            if (item.getLike_list() != null && item.getLike_list().size() > 0) {
                rvLike.setVisibility(View.VISIBLE);
                LikeListAdapter likeListAdapter = new LikeListAdapter(item.getLike_list());
                rvLike.setDatas(item.getLike_list());
            } else {
                rvLike.setVisibility(View.GONE);
            }
​
            if (item.getComments_list() != null && item.getComments_list().size() > 0) {
                rvComment.setVisibility(View.VISIBLE);
                rvComment.setList(item.getComments_list());
                rvComment.setOnCommentListener((position, bean, user_id) -> {
                    etComment.setText("");
                    //如果当前用户id和评论用户id相同则删除
                    if (bean.getCommentsUser().getUserId().equals("1")) {
          showDeletePopWindow(rvComment, Integer.parseInt(bean.getId()), helper.getLayoutPosition() - 1, position);
                        llComment.setVisibility(View.GONE);
                    } else {
                        //不相同回复
                        llComment.setVisibility(View.VISIBLE);
                        etComment.setHint("回复:" + bean.getCommentsUser().getUserName());
                        if (View.VISIBLE == llComment.getVisibility()) {
                            llComment.requestFocus();
                            //弹出键盘
                            KeyboardUtil.showSoftInput(etComment.getContext(), etComment);
                        } else if (View.GONE == llComment.getVisibility()) {
                            //隐藏键盘
                            KeyboardUtil.hideSoftInput(etComment.getContext(), etComment);
                        }
                        if (click != null) {
                            click.Commend(helper.getLayoutPosition() - 1, bean);
                        }
                    }
                });
                rvComment.notifyDataSetChanged();
            } else {
                rvComment.setVisibility(View.GONE);
            }
        } else {
            llLike.setVisibility(View.GONE);
        }
        rvComment.setOnItemLongClickListener((position, bean) -> {
            showCopyPopWindow(rvComment, item.getComments_list().get(position).getContent());
        });
    }
​
    private void showCopyPopWindow(CommentsView rvComment, String content) {
        View contentView = getCopyPopupWindowContentView(content);
        mPopupWindow = new PopupWindow(contentView,
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
        // 如果不设置PopupWindow的背景,有些版本就会出现一个问题:无论是点击外部区域还是Back键都无法dismiss弹框
        mPopupWindow.setBackgroundDrawable(new ColorDrawable());
        // 设置好参数之后再show
        int[] windowPos = PopupWindowUtil.calculatePopWindowPos(rvComment, contentView, x, y);
        mPopupWindow.showAsDropDown(rvComment, 0, -60, windowPos[1]);
        mPopupWindow.setFocusable(true);
        mPopupWindow.setOutsideTouchable(true);
    }
​
    private View getCopyPopupWindowContentView(String content) {
        // 布局ID
        int layoutId = R.layout.popup_copy;
        View contentView = LayoutInflater.from(mContext).inflate(layoutId, null);
        View.OnClickListener menuItemOnClickListener = v -> {
            if (mPopupWindow != null) {
                mPopupWindow.dismiss();
            }
            ClipboardManager mCM = (ClipboardManager) mContext.getSystemService(CLIPBOARD_SERVICE);
            mCM.setPrimaryClip(ClipData.newPlainText(null, content));
            Toast.makeText(mContext, mContext.getString(R.string.copied), Toast.LENGTH_SHORT).show();
        };
        contentView.findViewById(R.id.menu_copy).setOnClickListener(menuItemOnClickListener);
        return contentView;
    }
​
    private void showDeletePopWindow(View view, int id, int layoutPosition, int position) {
        View contentView = getPopupWindowContentView(id, layoutPosition, position);
        mPopupWindow = new PopupWindow(contentView,
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
        // 如果不设置PopupWindow的背景,有些版本就会出现一个问题:无论是点击外部区域还是Back键都无法dismiss弹框
        mPopupWindow.setBackgroundDrawable(new ColorDrawable());
        // 设置好参数之后再show
        int[] windowPos = PopupWindowUtil.calculatePopWindowPos(view, contentView, x, y);
        mPopupWindow.showAsDropDown(view, 0, -40, windowPos[1]);
        mPopupWindow.setFocusable(true);
        mPopupWindow.setOutsideTouchable(true);
    }
​
    private View getPopupWindowContentView(int id, int layoutPosition, int position) {
        // 布局ID
        int layoutId = R.layout.popup_delete;
        View contentView = LayoutInflater.from(mContext).inflate(layoutId, null);
        View.OnClickListener menuItemOnClickListener = v -> {
            if (mPopupWindow != null) {
                mPopupWindow.dismiss();
            }
​
        };
        contentView.findViewById(R.id.menu_delete).setOnClickListener(menuItemOnClickListener);
        return contentView;
    }
​
    public interface Click {
        //回复评论
        void Commend(int position, CommentListBean bean);
    }
}

4.MainActivity代码:

部分代码截图:

img

img

img

img

5.MainActivity完整代码:

/**
 * @作者: njb
 * @时间: 2019/7/22 10:53
 * @描述: 仿微信朋友圈列表
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener, CircleAdapter.Click, ImageWatcher.OnPictureLongPressListener, ImageWatcher.Loader {
    private RecyclerView recyclerView;
    private CircleAdapter circleAdapter;
    private String content;
    private LikePopupWindow likePopupWindow;
    private EditText etComment;
    private LinearLayout llComment;
    private TextView tvSend;
    private LinearLayout llScroll;
    private int editTextBodyHeight;
    private int currentKeyboardH;
    private int selectCommentItemOffset;
    private int commentPosition;
    protected final String TAG = this.getClass().getSimpleName();
    CompositeDisposable compositeDisposable;
    private TextView tvCity;
    private ScrollSpeedLinearLayoutManger layoutManger;
    private List<CircleBean.DataBean> dataBeans;
    ImageWatcher imageWatcher;
    private int isLike;
    private int comPosition;
    private String to_user_id;
    private String to_user_name;
    private String circle_id;
    private String userId;
    private String userName;
    private RxPermissions rxPermissions;
    private static MyHandler myHandler;
    private static String dstPath;
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        initData();
        initAdapter();
        setListener();
        initRxBus();
    }
​
    private void initRxBus() {
        compositeDisposable = new CompositeDisposable();
        RxBus.getInstance().toObservable(WeatherEvent.class)
                .subscribe(new Observer<WeatherEvent>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        compositeDisposable.add(d);
                    }
​
                    @Override
                    public void onNext(WeatherEvent weatherEvent) {
                        Log.e("weather", weatherEvent.getTemperature() + "-**-" + weatherEvent.getCityName());
                        tvCity.setText(String.format("%s %s", weatherEvent.getCityName(), weatherEvent.getTemperature()));
                    }
​
                    @Override
                    public void onError(Throwable e) {
​
                    }
​
                    @Override
                    public void onComplete() {
​
                    }
                });
    }
​
    private void setListener() {
        tvSend.setOnClickListener(this);
        etComment.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
​
            }
​
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                content = etComment.getText().toString();
                if (etComment.getText().length() == 500) {
                    Toast.makeText(MainActivity.this, getResources().getString(R.string.the_content_of_the_comment_cannot_exceed_500_words), Toast.LENGTH_SHORT).show();
                }
            }
​
            @Override
            public void afterTextChanged(Editable editable) {
            }
        });
        recyclerView.setOnTouchListener((view, motionEvent) -> {
            if (llComment.getVisibility() == View.VISIBLE) {
                updateEditTextBodyVisible(View.GONE);
                return true;
            }
            return false;
        });
        circleAdapter.setOnItemChildClickListener((adapter, view, position) -> {
            userId = Objects.requireNonNull(circleAdapter.getItem(position)).getId();
            userName = Objects.requireNonNull(circleAdapter.getItem(position)).getUser_name();
            isLike = Objects.requireNonNull(circleAdapter.getItem(position)).getIs_like();
            comPosition = position;
            switch (view.getId()) {
                case R.id.iv_edit:
                    //评论弹框
                    showLikePopupWindow(view, position);
                    break;
                case R.id.tv_delete:
                    //删除朋友圈
                    deleteCircleDialog();
                    break;
                default:
                    break;
            }
        });
    }
​
    private void showLikePopupWindow(View view, int position) {
        //item 底部y坐标
        final int mBottomY = getCoordinateY(view) + view.getHeight();
        if (likePopupWindow == null) {
            likePopupWindow = new LikePopupWindow(this, isLike);
        }
        likePopupWindow.setOnPraiseOrCommentClickListener(new OnPraiseOrCommentClickListener() {
            @Override
            public void onPraiseClick(int position) {
                //调用点赞接口
                likePopupWindow.dismiss();
            }
​
            @Override
            public void onCommentClick(int position) {
                llComment.setVisibility(View.VISIBLE);
                etComment.requestFocus();
                etComment.setHint("说点什么");
                to_user_id = null;
                KeyboardUtil.showSoftInput(MainActivity.this);
                likePopupWindow.dismiss();
                etComment.setText("");
                view.postDelayed(() -> {
                    int y = getCoordinateY(llComment) - 20;
                    //评论时滑动到对应item底部和输入框顶部对齐
                    recyclerView.smoothScrollBy(0, mBottomY - y);
                }, 300);
            }
​
            @Override
            public void onClickFrendCircleTopBg() {
​
            }
​
            @Override
            public void onDeleteItem(String id, int position) {
​
            }
​
        }).setTextView(isLike).setCurrentPosition(position);
        if (likePopupWindow.isShowing()) {
            likePopupWindow.dismiss();
        } else {
            likePopupWindow.showPopupWindow(view);
        }
    }
​
​
    private void deleteCircleDialog() {
        final AlertDialog.Builder alert = new AlertDialog.Builder(this);
        alert.setTitle("提示");
        alert.setMessage("你确定要删除吗?");
        alert.setNegativeButton("取消", null);
        alert.setPositiveButton("确定", (dialog, which) -> {
            //调接口删除
            dialog.dismiss();
        });
        alert.show();
    }
​
​
    private void setViewTreeObserver() {
        final ViewTreeObserver swipeRefreshLayoutVTO = llScroll.getViewTreeObserver();
        swipeRefreshLayoutVTO.addOnGlobalLayoutListener(() -> {
            Rect r = new Rect();
            llScroll.getWindowVisibleDisplayFrame(r);
            int statusBarH = Utils.getStatusBarHeight();//状态栏高度
            int screenH = llScroll.getRootView().getHeight();
            if (r.top != statusBarH) {
                //在这个demo中r.top代表的是状态栏高度,在沉浸式状态栏时r.top=0,通过getStatusBarHeight获取状态栏高度
                r.top = statusBarH;
            }
            int keyboardH = screenH - (r.bottom - r.top);
            Log.d(TAG, "screenH= " + screenH + " &keyboardH = " + keyboardH + " &r.bottom=" + r.bottom + " &top=" + r.top + " &statusBarH=" + statusBarH);
​
            if (keyboardH == currentKeyboardH) {//有变化时才处理,否则会陷入死循环
                return;
            }
            currentKeyboardH = keyboardH;
            editTextBodyHeight = llComment.getHeight();
            if (keyboardH < 150) {//说明是隐藏键盘的情况
                MainActivity.this.updateEditTextBodyVisible(View.GONE);
                return;
            }
        });
    }
​
    /**
     * 初始化控件
     */
    private void initViews() {
        recyclerView = findViewById(R.id.recyclerView);
        llComment = findViewById(R.id.ll_comment);
        etComment = findViewById(R.id.et_comment);
        tvSend = findViewById(R.id.tv_send_comment);
        llScroll = findViewById(R.id.ll_scroll);
        tvCity = findViewById(R.id.tv_city);
        imageWatcher = findViewById(R.id.imageWatcher);
        //初始化仿微信图片滑动加载器
        imageWatcher.setTranslucentStatus(Utils.calcStatusBarHeight(this));
        imageWatcher.setErrorImageRes(R.mipmap.error_picture);
        imageWatcher.setOnPictureLongPressListener(this);
        imageWatcher.setLoader(this);
        getPermissions();
        myHandler = new MyHandler(this);
    }
​
    /**
     * 初始化数据
     *
     * @param
     */
    private void initData() {
        dataBeans = new ArrayList<>();
        dataBeans = AssetsUtil.getStates(this);
    }
​
    /**
     * 设置adapter
     */
    private void initAdapter() {
        circleAdapter = new CircleAdapter(dataBeans, imageWatcher, llComment, etComment, this);
        layoutManger = new ScrollSpeedLinearLayoutManger(this);
        recyclerView.setLayoutManager(layoutManger);
        layoutManger.setSpeedSlow();
        recyclerView.addItemDecoration(new SpaceDecoration(this));
        recyclerView.setAdapter(circleAdapter);
    }
​
​
    public void updateEditTextBodyVisible(int visibility) {
        llComment.setVisibility(visibility);
        if (View.VISIBLE == visibility) {
            llComment.requestFocus();
            //弹出键盘
            CommonUtils.showSoftInput(etComment.getContext(), etComment);
​
        } else if (View.GONE == visibility) {
            //隐藏键盘
            CommonUtils.hideSoftInput(etComment.getContext(), etComment);
        }
    }
​
    /**
     * 获取控件左上顶点Y坐标
     *
     * @param view
     * @return
     */
    private int getCoordinateY(View view) {
        int[] coordinate = new int[2];
        view.getLocationOnScreen(coordinate);
        return coordinate[1];
    }
​
​
    @Override
    public void onClick(View view) {
        int i = view.getId();
        if (i == R.id.tv_send_comment) {
            if (TextUtils.isEmpty(etComment.getText().toString())) {
                Toast.makeText(MainActivity.this, "请输入评论内容", Toast.LENGTH_SHORT).show();
                return;
            }
            //请求接口,在成功回调方法拼接评论信息,这里写死
            getComment();
            setViewTreeObserver();
        }
    }
​
    private void getComment() {
        List<CircleBean.DataBean> list = circleAdapter.getData();
        CommentListBean commentListBean = new CommentListBean();
        //userId为当前用户id,这里只是一个例子所以没有登录注册
        commentListBean.setUser_id(userId);
        //userName为当前用户名称
        commentListBean.setUser_name(userName);
        commentListBean.setTo_user_name(TextUtils.isEmpty(to_user_name) ? "" : to_user_name);
        commentListBean.setTo_user_id(TextUtils.isEmpty(to_user_id) ? "" : to_user_id);
        commentListBean.setCircle_id(circle_id);
        commentListBean.setContent(content);
        if (TextUtils.isEmpty(to_user_id)) {
            ToastUtils.ToastShort("评论成功");
            list.get(comPosition).getComments_list().add(commentListBean);
        } else {
            ToastUtils.ToastShort("回复成功");
            list.get(commentPosition).getComments_list().add(commentListBean);
        }
        circleAdapter.notifyDataSetChanged();
        KeyboardUtil.hideSoftInput(MainActivity.this);
        llComment.setVisibility(View.GONE);
        etComment.setText("");
    }
​
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //取消订阅
        RxBus.rxBusUnbund(compositeDisposable);
        myHandler.removeCallbacksAndMessages(null);
    }
​
    @Override
    public void Commend(int position, CommentListBean bean) {
        circle_id = bean.getCircle_id();
        commentPosition = position;
        to_user_name = bean.getUser_name();
        to_user_id = bean.getUser_id();
    }
​
    @Override
    public void load(Context context, Uri uri, ImageWatcher.LoadCallback loadCallback) {
        Glide.with(context).asBitmap().load(uri.toString()).into(new GlideSimpleTarget(loadCallback));
    }
​
    @Override
    public void onPictureLongPress(ImageView v, Uri uri, int pos) {
        final AlertDialog.Builder alert = new AlertDialog.Builder(this);
        alert.setTitle("保存图片");
        alert.setMessage("你确定要保存图片吗?");
        alert.setNegativeButton("取消", null);
        alert.setPositiveButton("确定", (dialog, which) -> {
            rxPermissions
                    .request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    .subscribe(granted -> {
                        if (granted) {
                            if (uri != null) {// Always true pre-M
                                savePhoto(uri);
                            }
                        } else {
                            ToastUtils.ToastShort("缺少必要权限,请授予权限");
                        }
                    });
            dialog.dismiss();
​
        });
        alert.show();
    }
​
    @SuppressLint("HandlerLeak")
    private class MyHandler extends Handler {
        private WeakReference<Activity> mActivity;
        private Bitmap bitmap;
​
        private MyHandler(Activity activity) {
            mActivity = new WeakReference<>(activity);
        }
​
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            final Activity activity = mActivity.get();
            if (activity != null) {
                if (msg.what == 1) {
                    try {
                        bitmap = (Bitmap) msg.obj;
                        if (Utils.saveBitmap(bitmap, dstPath, false)) {
                            try {
                                ContentValues values = new ContentValues(2);
                                values.put(MediaStore.Images.Media.MIME_TYPE, Constants.MIME_JPEG);
                                values.put(MediaStore.Images.Media.DATA, dstPath);
                                getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
                                ToastUtils.ToastShort(activity, getResources().getString(R.string.picture_save_to));
                            } catch (Exception e) {
                                ToastUtils.ToastShort(activity, getResources().getString(R.string.picture_save_fail));
                            }
                        } else {
                            ToastUtils.ToastShort(activity, getResources().getString(R.string.picture_save_fail));
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        ToastUtils.ToastShort(activity, getResources().getString(R.string.picture_save_fail));
                    }
                }
            }
        }
    }
​
    private void getPermissions() {
        rxPermissions = new RxPermissions(this);
    }
​
    /**
     * 长按保存图片
     * @param uri 图片url地址
     */
    private void savePhoto(Uri uri) {
        Glide.with(MainActivity.this).asBitmap().load(uri).listener(new RequestListener<Bitmap>() {
            @Override
            public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
                String picPath = StorageUtil.getSystemImagePath();
                dstPath = picPath + (System.currentTimeMillis() / 1000) + ".jpeg";
                Message message = Message.obtain();
                message.what = 1;
                message.obj = resource;
                myHandler.sendMessage(message);
                return false;
            }
​
            @Override
            public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
                return false;
            }
        }).submit();
    }

6.视频播放:

img

7.视频播放Activity,部分代码截图:

img

8.视频播放完整代码:

/**
 * @作者: njb
 * @时间: 2020/1/2 14:02
 * @描述: 视频播放
 */
public class PlayVideoActivity extends AppCompatActivity {
    private String url;
    private IjkVideoView videoView;
​
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_play_video);
        initData();
        initViews();
        initPlayer();
        startPlay();
    }
​
    private void initViews() {
        //播放view
        videoView = findViewById(R.id.video_view);
    }
​
    private void initData() {
        if(getIntent()!= null&& getIntent().getExtras()!= null){
            //视频url
            url = getIntent().getExtras().getString("url");
        }
    }
​
    /**
     * 初始化Player
     */
    private void initPlayer() {
        com.dueeeke.videocontroller.StandardVideoController controller = new StandardVideoController(this);
        IjkPlayer ijkPlayer = new IjkPlayer(this) {
            @Override
            public void setOptions() {
                super.setOptions();
                mMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", 1);
            }
        };
        videoView.setPlayerConfig(new PlayerConfig.Builder().setCustomMediaPlayer(ijkPlayer).build());
        videoView.setVideoController(controller);
    }
​
    /**
     * 开始播放
     */
    private void startPlay() {
        if (url != null) {
            videoView.setUrl(url);
            videoView.start();
            videoView.isFullScreen();
        }
    }
​
​
    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        //横竖屏切换
        refresh();
    }
​
    /**
     * 横竖屏切换刷新
     */
    private void refresh() {
        boolean isBaseOnWidth = (getResources().getDisplayMetrics().widthPixels <= getResources().getDisplayMetrics().heightPixels);
        Window window  = getWindow();
        if(window != null){
            window.getDecorView().post(() -> {
                window.getDecorView().setVisibility(View.GONE);
                AutoSizeCompat.autoConvertDensity(getResources(),420,isBaseOnWidth);
                window.getDecorView().setVisibility(View.VISIBLE);
            });
        }
    }
​
    @Override
    public void onResume() {
        super.onResume();
        if (videoView != null) {
            videoView.resume();
        }
    }
​
    @Override
    public void onPause() {
        super.onPause();
        if (videoView != null) {
            videoView.pause();
        }
    }
​
    @Override
    public void onBackPressed() {
        if (!videoView.onBackPressed()) {
            super.onBackPressed();
        }
        super.onBackPressed();
    }
​
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //释放播放器
        if (videoView != null) {
            videoView.release();
            videoView = null;
        }
    }
}
​

9.总结:

图片保存刚开始没用线程,导致要保存2次才能成功,后面改为handler发消息,后面会加上视频播放和点击item跳转网页详情,这一篇也是在前几篇的基础上,解决了很多问题,比如适配9.0http访问网络、约束布局的使用、评论时定位到当前位置、使用第三方状态栏库导致输入框焦点不弹出、评论成功后不是请求后台数据而是刷新本地数据等等.本例子是写的假数据,实时请求还需修改请求逻辑,基本的逻辑都写好了,如有问题及时反馈,我会积极改进.

10.最后放几张完整的效果图:

img

img

img

img

11.demo完整代码地址:

ExpandTextView: 实现仿微信朋友圈列表多类型布局,图片点击放大、保存,包含点赞、评论、消息提醒、视频播放等功能

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐