Android仿微信朋友圈5实现朋友圈列表
微信朋友圈网上有很多开源的例子,本文是自己手写,之前的几篇都是单个功能,感觉很零散,这次放出完整的点赞评论和朋友圈列表功能.1.定义四种item类型,和后台接口匹配:2.CircleAdapter实现代码:部分核心代码截图:完整代码:/*** @作者: njb* @时间: 2019/7/25 10:47* @描述: 朋友圈适配器*/public ...
·
前言:
微信朋友圈网上有很多开源的例子,本文是自己手写,之前的几篇都是单个功能,感觉很零散,这次放出完整的点赞评论和朋友圈列表功能,大家有兴趣可以看一下
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;
}
@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代码:
部分代码截图:

![]()

![]()

![]()

![]()
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.视频播放:

![]()
7.视频播放Activity,部分代码截图:

![]()
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.最后放几张完整的效果图:

![]()

![]()

![]()

![]()
11.demo完整代码地址:
ExpandTextView: 实现仿微信朋友圈列表多类型布局,图片点击放大、保存,包含点赞、评论、消息提醒、视频播放等功能
更多推荐


所有评论(0)