前言:

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

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

2.CircleAdapter实现代码:

部分核心代码截图:

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;}
​@Overrideprotected 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());// 设置好参数之后再showint[] 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) {// 布局IDint 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());// 设置好参数之后再showint[] 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) {// 布局IDint 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代码:

部分代码截图:

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;
​@Overrideprotected 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>() {@Overridepublic void onSubscribe(Disposable d) {compositeDisposable.add(d);}
​@Overridepublic void onNext(WeatherEvent weatherEvent) {Log.e("weather", weatherEvent.getTemperature() + "-**-" + weatherEvent.getCityName());tvCity.setText(String.format("%s %s", weatherEvent.getCityName(), weatherEvent.getTemperature()));}
​@Overridepublic void onError(Throwable e) {
​}
​@Overridepublic void onComplete() {
​}});}
​private void setListener() {tvSend.setOnClickListener(this);etComment.addTextChangedListener(new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
​}
​@Overridepublic 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();}}
​@Overridepublic 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() {@Overridepublic void onPraiseClick(int position) {//调用点赞接口likePopupWindow.dismiss();}
​@Overridepublic 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);}
​@Overridepublic void onClickFrendCircleTopBg() {
​}
​@Overridepublic 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];}
​
​@Overridepublic 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("");}
​@Overrideprotected void onDestroy() {super.onDestroy();//取消订阅RxBus.rxBusUnbund(compositeDisposable);myHandler.removeCallbacksAndMessages(null);}
​@Overridepublic 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();}
​@Overridepublic void load(Context context, Uri uri, ImageWatcher.LoadCallback loadCallback) {Glide.with(context).asBitmap().load(uri.toString()).into(new GlideSimpleTarget(loadCallback));}
​@Overridepublic 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-MsavePhoto(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);}
​@Overridepublic 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>() {@Overridepublic 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;}
​@Overridepublic boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {return false;}}).submit();}

6.视频播放:

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

8.视频播放完整代码:

/*** @作者: njb* @时间: 2020/1/2 14:02* @描述: 视频播放*/
public class PlayVideoActivity extends AppCompatActivity {private String url;private IjkVideoView videoView;
​@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_play_video);initData();initViews();initPlayer();startPlay();}
​private void initViews() {//播放viewvideoView = findViewById(R.id.video_view);}
​private void initData() {if(getIntent()!= null&& getIntent().getExtras()!= null){//视频urlurl = getIntent().getExtras().getString("url");}}
​/*** 初始化Player*/private void initPlayer() {com.dueeeke.videocontroller.StandardVideoController controller = new StandardVideoController(this);IjkPlayer ijkPlayer = new IjkPlayer(this) {@Overridepublic 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();}}
​
​@Overridepublic 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);});}}
​@Overridepublic void onResume() {super.onResume();if (videoView != null) {videoView.resume();}}
​@Overridepublic void onPause() {super.onPause();if (videoView != null) {videoView.pause();}}
​@Overridepublic void onBackPressed() {if (!videoView.onBackPressed()) {super.onBackPressed();}super.onBackPressed();}
​@Overrideprotected void onDestroy() {super.onDestroy();//释放播放器if (videoView != null) {videoView.release();videoView = null;}}
}
​

9.总结:

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

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

11.demo完整代码地址:

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

Android仿微信朋友圈5实现朋友圈列表相关推荐

  1. Android仿微信首页下拉显示小程序列表

    花点时间重新熟悉一下AndroidUI方面的东西,把古董PullToRefreshView又撸了一遍,技术这种东西真是忘得快啊...在基础上新增一点东西,粗糙地实现了仿微信首页下拉显示小程序列表的样式 ...

  2. android微信下拉出现小程序,Android仿微信首页下拉显示小程序列表

    花点时间重新熟悉一下AndroidUI方面的东西,把古董PullToRefreshView又撸了一遍,技术这种东西真是忘得快啊...在基础上新增一点东西,粗糙地实现了仿微信首页下拉显示小程序列表的样式 ...

  3. android 仿微信朋友圈 评论,2020年android 仿微信朋友圈 评论

    2020年android 仿微信朋友圈 评论 1.如果有人问我:那些艰难的岁月你是怎么熬过来的?我想我只有一句话回答:我有一种强大的精神力量支撑着我,这种力量名字叫"想死又不敢" ...

  4. Android自定义弹窗模仿微信,Android 仿微信朋友圈点赞和评论弹出框功能

    本文简单模仿微信朋友圈的点赞和评论弹出框,布局等细节请忽略,着重实现弹出框.发评论,及弹出位置的控制. 1. 微信弹出框 微信朋友圈的点赞和评论功能,有2个组成部分: 点击左下角的"更多&q ...

  5. Android仿微信朋友圈发图片和文字

    Android仿微信朋友圈发图片和文字的一个开源项目,其在github上的项目主页是:https://github.com/zhangphil/FangWeiXinPengYouQuanFaTuPia ...

  6. android 查找朋友圈,Android仿微信朋友圈点击评论自动定位到相关行功能

    最近闲来无事,随便看看各种UI实现的代码 打开你的微信朋友圈,点击评论,你就会发现有一个小细节:文本输入框的高度恰好定位到这条信息的底部位置 这个实现起来其实很简单,咱们就来看看吧 最简单的Recyc ...

  7. android 微信评论功能,Android仿微信朋友圈点击评论自动定位到相关行功能

    最近闲来无事,随便看看各种UI实现的代码 打开你的微信朋友圈,点击评论,你就会发现有一个小细节:文本输入框的高度恰好定位到这条信息的底部位置 这个实现起来其实很简单,咱们就来看看吧 最简单的Recyc ...

  8. android 微信评论功能,Android仿微信朋友圈点击评论自动定位到相关行功能.pdf

    Android仿仿微微信信朋朋友友圈圈点点击击评评论论自自动动定定位位到到相相关关行行功功能能 这篇文章主要介绍了android仿微信朋友圈点击评论自动定位到相关行功能的实现,本文图文并茂给大家介绍的 ...

  9. Android仿微信发布朋友圈图片多选器实现

    Android仿微信发布朋友圈图片多选器实现 前段时间由于项目需要,自己弄了一个仿微信朋友圈图片多选的功能,支持图片单选/多选.支持视频.图片的扫描 和视频图片的预览.并且封装功能模块后上传到了 ji ...

  10. android2.3 微信,Android新版微信2.3支持周边朋友查找

    Android新版微信2.3支持周边朋友查找 2011年08月16日 09:02作者:陈涛编辑:陈涛文章出处:泡泡网原创 分享 泡泡网软件频道8月16日 微信是一款手机通信软件,支持通过手机网络发送语 ...

最新文章

  1. oracle导出超过100m的数据库,Oracle数据库的导出和导入
  2. Web常见约定规范(精选)
  3. 数据库路由中间件MyCat - 源代码篇(7)
  4. 为什么二进制保存的文件,打开仍然可以正常显示
  5. C语言学习之将一个数组中的值按逆序重新存放。例如,原来顺序为8,6,5,4,1. 要求改为1,4,5,6,8。
  6. Django框架基础知识点
  7. 奥鹏20春在线作业c语言,电子科20春《C语言(专科)》在线作业3答案
  8. Linux之rsync
  9. .net core 2.0学习笔记(三):度量.net framework 迁移到.net core的工作量
  10. 已知起点坐标、角度、长度求终点坐标
  11. Linux下搭建mysql主从服务器
  12. Android 签名时 v2 与 v1 的选择
  13. 选择正确的C/C++ runtime library
  14. JS动态添加元素绑定点击事件,触发事件多次执行问题?
  15. 【NOIP2017】滚粗记
  16. ignore在mysql中什么意思_ignore是什么意思
  17. latex转word_最有效率的论文排版利器来了,word排版再见吧~
  18. python-requests 模拟登陆京东
  19. Node 异步I/O 实现
  20. Android解析lrc里的歌词

热门文章

  1. 大规模定制家具实施ERP的必要性
  2. 妙用自定义注解,一行代码搞定大功能(文末赠书)
  3. 清明节网站都变灰了,你知道是怎么做的么?
  4. 运营_APP的常见盈利模式
  5. fiddler安卓模拟器与ios手机抓包
  6. archlinux自定义安装教程(一)
  7. Java8 JDK8
  8. 我打不了字计算机应用怎么办,键盘正常为什么打不了字 电脑键盘失灵怎么解决...
  9. 初中数学分几个模块_初中数学分成三大模块
  10. 吴恩达 02.改善深层神经网络:超参数调试、正则化以及优化 第一周作业