Android 仿微信朋友圈點(diǎn)贊和評(píng)論彈出框功能
貢獻(xiàn)/下載源碼:https://github.com/mmlovesyy/PopupWindowDemo
本文簡(jiǎn)單模仿微信朋友圈的點(diǎn)贊和評(píng)論彈出框,布局等細(xì)節(jié)請(qǐng)忽略,著重實(shí)現(xiàn)彈出框、發(fā)評(píng)論,及彈出位置的控制。
1. 微信彈出框
微信朋友圈的點(diǎn)贊和評(píng)論功能,有2個(gè)組成部分:
點(diǎn)擊左下角的“更多”按鈕,彈出對(duì)話(huà)框;
點(diǎn)擊評(píng)論,彈出輸入框,添加評(píng)論并在頁(yè)面中實(shí)時(shí)顯示;
微信朋友圈點(diǎn)贊和評(píng)論功能
2. 實(shí)際效果
本文將建一個(gè) ListView,在其 Item 中簡(jiǎn)單模仿微信的布局,然后著重實(shí)現(xiàn)彈出窗,并能發(fā)評(píng)論,忽略具體布局細(xì)節(jié)。具體效果如下:
3. 知識(shí)點(diǎn)清單
ListView
自定義 Adapter,重寫(xiě) getView()方法;
PopupWindow
彈出框使用PopupWindow實(shí)現(xiàn),這是點(diǎn)贊和評(píng)論的載體,具體要涉及 PopupWindow 點(diǎn)擊非窗口位置和再次點(diǎn)擊消失以及顯示位置的問(wèn)題(根據(jù)相應(yīng)更多按鈕的位置確定 PopupWindow 的顯示位置,關(guān)于 PopupWindow 的顯示位置,可以參考我的另一篇文章 Android PopupWindow 的顯示位置);
LayoutInflater
使用LayoutInflater 動(dòng)態(tài)加載PopupWindow 的布局,關(guān)于 LayoutInflater 的更多知識(shí),參見(jiàn)我的另一篇博客 Android LayoutInflater ;
Activity 和 Item 的雙向通信
通過(guò)自定義 OnCommentListener() 來(lái)實(shí)現(xiàn) MainActivity(具體來(lái)說(shuō)是屏幕底部評(píng)論框中的輸入的內(nèi)容)和 ItemView(動(dòng)態(tài)的獲得上述輸入的評(píng)論內(nèi)容并展示在該ItemView 中) 的通信,更多知識(shí)參見(jiàn)我的另一篇博客《 燕過(guò)留聲:由 Activity 和 Fragment 的通信方法想到的》;
自定義控件
ListView 中的每個(gè) Item 是一個(gè)自定義的 ItemView,記得要重寫(xiě)構(gòu)造方法,否則會(huì)拋出 Android.view.InflateException 異常;
如果想實(shí)現(xiàn)微信評(píng)論那樣用戶(hù)名和內(nèi)容回復(fù)文字字體顏色不同,而且點(diǎn)擊評(píng)論用戶(hù)名觸發(fā)頁(yè)面跳轉(zhuǎn)等功能,請(qǐng)參見(jiàn) 《布局優(yōu)化技巧筆記》 之 ClickableSpan 章節(jié);
4. 美工素材
由于 .apk 本質(zhì)上是個(gè)壓縮包,我們可以通過(guò)解壓得到該 .apk 文件的圖片素材和布局文件,更多獲得素材的方法參見(jiàn)我的另一篇博文 如何獲得Android素材圖片。通過(guò)這種方式得到顏色、更多按鈕的樣式等素材,僅供學(xué)習(xí)之用,請(qǐng)勿做侵犯版權(quán)之事。尊重知識(shí)版權(quán)既是大勢(shì)所趨,也是終將使每個(gè)開(kāi)發(fā)者受益的事。
文件夾r里存放圖片
找到更多按鈕
5. 關(guān)鍵代碼
開(kāi)發(fā)環(huán)境:Android Studio 1.4.1 for Mac + ADT 21 + JDK 1.8.0。
MainAcitivity.Java
package main.zhaizu.com.popupwindowdemo; import android.content.Context; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.ListView; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import main.zhaizu.com.popupwindowdemo.model.Comment; import main.zhaizu.com.popupwindowdemo.model.Item; import main.zhaizu.com.popupwindowdemo.ui.ItemView; public class MainActivity extends AppCompatActivity { private ListView mListView; private View mCommentView; private MyAdapter myAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mListView = (ListView) findViewById(R.id.listview); myAdapter = new MyAdapter(this, getData()); mListView.setAdapter(myAdapter); mCommentView = findViewById(R.id.comment_view); } // build data private ArrayList<Item> getData() { int ITEM_COUNT = 20; ArrayList<Item> data = new ArrayList<>(); data.add(new Item(R.drawable.xiaona, "薄荷栗", "我學(xué)過(guò)跆拳道,都給我跪下唱征服", "昨天")); data.add(new Item(R.drawable.xueyan, "欣然", "走遍天涯海角,唯有我家風(fēng)景最好,啊哈哈", "昨天")); data.add(new Item(R.drawable.leishao, "陳磊_CL", "老子以后要當(dāng)行長(zhǎng)的,都來(lái)找我借錢(qián)吧,now", "昨天")); data.add(new Item(R.drawable.yuhong, "永恒依然", "房子車(chē)子都到碗里來(lái)", "昨天")); data.add(new Item(R.drawable.lanshan, "藍(lán)珊", "你們這群傻×,我笑而不語(yǔ)", "昨天")); return data; } // custom adapter private class MyAdapter extends BaseAdapter implements ItemView.OnCommentListener { private Context context; private ArrayList<Item> mData; private Map<Integer, ItemView> mCachedViews = new HashMap<>(); public MyAdapter(Context context, ArrayList<Item> mData) { this.context = context; this.mData = mData; } @Override public int getCount() { return mData.size(); } @Override public Object getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView != null) { view = convertView; } else { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.listview_item, null, false); } if (view instanceof ItemView) { Item data = (Item) getItem(position); ((ItemView) view).setData(data); ((ItemView) view).setPosition(position); ((ItemView) view).setCommentListener(this); cacheView(position, (ItemView) view); } return view; } @Override public void onComment(int position) { showCommentView(position); } private void cacheView(int position, ItemView view) { Iterator<Map.Entry<Integer, ItemView>> entries = mCachedViews.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<Integer, ItemView> entry = entries.next(); if (entry.getValue() == view && entry.getKey() != position) { mCachedViews.remove(entry.getKey()); break; } } mCachedViews.put(position, view); } private void showCommentView(final int position) { mCommentView.setVisibility(View.VISIBLE); mCommentView.findViewById(R.id.submit).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EditText et = (EditText) mCommentView.findViewById(R.id.edit); String s = et.getText().toString(); if (!TextUtils.isEmpty(s)) { // update model Comment comment = new Comment(s); mData.get(position).getComments().add(comment); // update view maybe ItemView itemView = mCachedViews.get(position); if (itemView != null && position == itemView.getPosition()) { itemView.addComment(); } et.setText(""); mCommentView.setVisibility(View.GONE); } } }); } } }
ItemView.java
package main.zhaizu.com.popupwindowdemo.ui; import android.content.Context; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupWindow; import android.widget.TextView; import main.zhaizu.com.popupwindowdemo.R; import main.zhaizu.com.popupwindowdemo.model.Comment; import main.zhaizu.com.popupwindowdemo.model.Item; /** * Created by cmm on 15/10/31. */ public class ItemView extends LinearLayout implements View.OnClickListener { private int mPosition; private Item mData; private ImageView mPortraitView; private TextView mUserNameView; private TextView mContentView; private TextView mCreatedAtView; private LinearLayout mCommentLayout; private View mMoreView; private PopupWindow mMorePopupWindow; private int mShowMorePopupWindowWidth; private int mShowMorePopupWindowHeight; private OnCommentListener mCommentListener; public ItemView(Context context) { super(context); } public ItemView(Context context, AttributeSet attrs) { super(context, attrs); } public interface OnCommentListener { void onComment(int position); } @Override protected void onFinishInflate() { super.onFinishInflate(); mPortraitView = (ImageView) findViewById(R.id.portrait); mUserNameView = (TextView) findViewById(R.id.nick_name); mContentView = (TextView) findViewById(R.id.content); mCreatedAtView = (TextView) findViewById(R.id.created_at); mCommentLayout = (LinearLayout) findViewById(R.id.comment_layout); mMoreView = findViewById(R.id.more_btn); } public void setPosition(int mPosition) { this.mPosition = mPosition; } public void setCommentListener(OnCommentListener l) { this.mCommentListener = l; } public void setData(Item data) { mData = data; mPortraitView.setImageResource(data.getPortraitId()); mUserNameView.setText(data.getNickName()); mContentView.setText(data.getContent()); updateComment(); mMoreView.setOnClickListener(this); } /** * 彈出點(diǎn)贊和評(píng)論框 * * @param moreBtnView */ private void showMore(View moreBtnView) { if (mMorePopupWindow == null) { LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View content = li.inflate(R.layout.layout_more, null, false); mMorePopupWindow = new PopupWindow(content, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); mMorePopupWindow.setBackgroundDrawable(new BitmapDrawable()); mMorePopupWindow.setOutsideTouchable(true); mMorePopupWindow.setTouchable(true); content.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); mShowMorePopupWindowWidth = content.getMeasuredWidth(); mShowMorePopupWindowHeight = content.getMeasuredHeight(); View parent = mMorePopupWindow.getContentView(); TextView like = (TextView) parent.findViewById(R.id.like); TextView comment = (TextView) parent.findViewById(R.id.comment); // 點(diǎn)贊的監(jiān)聽(tīng)器 comment.setOnClickListener(this); } if (mMorePopupWindow.isShowing()) { mMorePopupWindow.dismiss(); } else { int heightMoreBtnView = moreBtnView.getHeight(); mMorePopupWindow.showAsDropDown(moreBtnView, -mShowMorePopupWindowWidth, -(mShowMorePopupWindowHeight + heightMoreBtnView) / 2); } } private void updateComment() { if (mData.hasComment()) { mCommentLayout.removeAllViews(); mCommentLayout.setVisibility(View.VISIBLE); for (Comment c : mData.getComments()) { TextView t = new TextView(getContext()); t.setLayoutParams(new LinearLayout.LayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))); t.setBackgroundColor(getResources().getColor(R.color.colorCommentLayoutBg)); t.setTextSize(16); t.setPadding(5, 2, 0, 3); t.setLineSpacing(3, (float) 1.5); t.setText(c.getComment()); mCommentLayout.addView(t); } } else { mCommentLayout.setVisibility(View.GONE); } } @Override public void onClick(View v) { int id = v.getId(); if (id == R.id.more_btn) { showMore(v); } else if (id == R.id.comment) { if (mCommentListener != null) { mCommentListener.onComment(mPosition); if (mMorePopupWindow != null && mMorePopupWindow.isShowing()) { mMorePopupWindow.dismiss(); } } } } public int getPosition() { return mPosition; } public void addComment() { updateComment(); } }
以上所述是小編給大家介紹的Android 仿微信朋友圈點(diǎn)贊和評(píng)論彈出框功能,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Android沉浸式狀態(tài)欄的實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了Android沉浸式狀態(tài)欄的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09Android端實(shí)現(xiàn)單點(diǎn)登錄的方法詳解
所謂單點(diǎn)登錄就是指的同一個(gè)賬戶(hù)(id)不能在一個(gè)以上的設(shè)備上登錄對(duì)應(yīng)的用戶(hù)系統(tǒng)(排除web端和移動(dòng)端可以同時(shí)登錄的情況),例如:用戶(hù)m在A設(shè)備登錄并保持登錄狀態(tài),然后又在B設(shè)備登錄,此時(shí)A應(yīng)該要強(qiáng)制下線(xiàn),m無(wú)法在A設(shè)備上繼續(xù)執(zhí)行用戶(hù)相關(guān)的操作,下面來(lái)一起看看吧。2016-11-11Android跳轉(zhuǎn)到通訊錄獲取用戶(hù)名稱(chēng)和手機(jī)號(hào)碼的實(shí)現(xiàn)思路
這篇文章主要介紹了Android跳轉(zhuǎn)到通訊錄獲取用戶(hù)名稱(chēng)和手機(jī)號(hào)碼的實(shí)現(xiàn)思路,當(dāng)用戶(hù)點(diǎn)擊跳轉(zhuǎn)到通訊錄界面 并取通訊錄姓名和手機(jī)號(hào)碼 ,實(shí)現(xiàn)代碼簡(jiǎn)單易懂,非常不錯(cuò)感興趣的朋友一起看看吧2016-10-10Moshi?完美解決Gson在kotlin中默認(rèn)值空的問(wèn)題詳解
這篇文章主要為大家介紹了Moshi?完美解決Gson在kotlin中默認(rèn)值空的問(wèn)題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Android 實(shí)現(xiàn)銀聯(lián)刷卡機(jī)消費(fèi)后手動(dòng)簽名的功能(示例代碼)
在一些商場(chǎng)購(gòu)物時(shí),不需要用筆在銀聯(lián)機(jī)上簽名了,直接用手指觸摸實(shí)現(xiàn)消費(fèi)簽名,非常方便,下面小編給大家分享Android 實(shí)現(xiàn)銀聯(lián)刷卡機(jī)消費(fèi)后手動(dòng)簽名的功能,需要的朋友參考下吧2017-12-12關(guān)于Android Activity之間傳遞數(shù)據(jù)的6種方式
這篇文章主要介紹了關(guān)于Android Activity之間傳遞數(shù)據(jù)的6種方式,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03Android getJSONObject與optJSONObject的區(qū)別結(jié)合源碼分析
這篇文章主要介紹了Android getJSONObject與optJSONObject的區(qū)別,結(jié)合源碼分析的相關(guān)資料,需要的朋友可以參考下2017-02-02Android自定義控件ScrollView實(shí)現(xiàn)上下滑動(dòng)功能
這篇文章主要為大家詳細(xì)介紹了Android自定義控件ScrollView實(shí)現(xiàn)上下滑動(dòng)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07Android App中實(shí)現(xiàn)簡(jiǎn)單的刮刮卡抽獎(jiǎng)效果的實(shí)例詳解
這篇文章主要介紹了Android App中實(shí)現(xiàn)簡(jiǎn)單的刮刮卡抽獎(jiǎng)效果的實(shí)例詳解,文中主要借助Bitmap的canvas.drawPath的api來(lái)實(shí)現(xiàn),需要的朋友可以參考下2016-03-03