1. 程式人生 > >Android評論彈出窗根據輸入法自適應高度

Android評論彈出窗根據輸入法自適應高度

需求分析:當我們點選輸入框時,會調出輸入法軟鍵盤,如果不做處理,PopupWindow評論視窗可能會擠到螢幕最上方,更糟糕的事件是看不到我們的輸入框,連自己輸入什麼內容都看不到,這樣使用者體驗非常差!下面先讓大家看我們做出來的效果圖:

上圖可以看出,輸入法彈出和隱藏,對於我們的評論視窗高度一直都是保持不變,我們預設設定為螢幕的80%。

這裡提供一下Demo原始碼給大家,歡迎下載:

二、首先,我們重寫了RelativeLayout根佈局,用來監聽輸入法狀態

KeyboardLayout.java

package com.t20.commentdemo.view;

import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.RelativeLayout;

//帶有鍵盤監聽的RelativeLayout
public class KeyboardLayout extends RelativeLayout {

	private KeyboardLayoutListener mListener;
	private boolean mIsKeyboardActive = false; //  輸入法是否啟用
	private int mKeyboardHeight = 0; // 輸入法高度
	private Context mContext;

	public KeyboardLayout(Context context) {
		this(context, null, 0);
	}

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

	public KeyboardLayout(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		mContext = context;
		// 監聽佈局變化
		getViewTreeObserver().addOnGlobalLayoutListener(
				new KeyboardOnGlobalChangeListener());
	}

	private class KeyboardOnGlobalChangeListener implements
			OnGlobalLayoutListener {

		int mScreenHeight = 0;

		private int getScreenHeight() {
			if (mScreenHeight > 0) {
				return mScreenHeight;
			}
			mScreenHeight = ((Activity) mContext).getWindowManager()
					.getDefaultDisplay().getHeight();
			return mScreenHeight;
		}

		@Override
		// 檢視樹中全域性佈局發生改變或者檢視樹中的某個檢視的可視狀態發生改變時呼叫
		public void onGlobalLayout() {
			Rect rect = new Rect();
			// 獲取當前頁面視窗的顯示範圍
			((Activity) getContext()).getWindow().getDecorView()
					.getWindowVisibleDisplayFrame(rect);
			int screenHeight = getScreenHeight();
			int keyboardHeight = screenHeight - rect.bottom; // 輸入法的高度
			boolean isActive = false;
			if (Math.abs(keyboardHeight) > screenHeight / 5) {
				isActive = true; // 超過螢幕五分之一則表示彈出了輸入法
				mKeyboardHeight = keyboardHeight;
			}
			mIsKeyboardActive = isActive;
			if (mListener != null) {
				mListener.onKeyboardStateChanged(isActive, keyboardHeight);
			}
		}
	}

	public void setKeyboardListener(KeyboardLayoutListener listener) {
		mListener = listener;
	}

	public KeyboardLayoutListener getKeyboardListener() {
		return mListener;
	}

	public boolean isKeyboardActive() {
		return mIsKeyboardActive;
	}

	/**
	 * 獲取輸入法高度
	 * 
	 * @return
	 */
	public int getKeyboardHeight() {
		return mKeyboardHeight;
	}

	public interface KeyboardLayoutListener {
		/**
		 * @param isActive
		 *            輸入法是否啟用
		 * @param keyboardHeight
		 *            輸入法面板高度
		 */
		void onKeyboardStateChanged(boolean isActive, int keyboardHeight);
	}

}

二、其次,我們自定義了一個PopupWindow,方便以後直接引用到其他專案中

CommentPopupWindow.java

package com.t20.commentdemo.view;

import com.t20.commentdemo.R;
import com.t20.commentdemo.view.KeyboardLayout.KeyboardLayoutListener;

import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class CommentPopupWindow extends PopupWindow {
	private Context mContext;
	private OnClickListener mOnClickListener;
	private OnFocusChangeListener mOnFocusChangeListener;

	private View mPopupWindowView;
	// popupWindow對應的佈局
	private RelativeLayout mPopupWindowViewLayout;
	// 整個螢幕的寬度
	private int mScreenWidth;
	// 整個螢幕的高度
	private int mScreenHeight;
	// popupWindow對應的佈局高度佔螢幕的比例
	private final double mPopupWindowHeightForScreenPercent = 0.8;
	private LinearLayout mCommentPopupWindowHead;
	private LinearLayout mCommentPopupWindowFoot;
	//評論總數
	private TextView mTextViewCommentCount;
	//關閉視窗
	private TextView mTextViewClose;
	//評論的列表檢視
	private ListView mListViewComments;
	//輸入的評論內容
	private EditText mEditTextInput;
	//傳送按鈕
	private TextView mTextViewSend;

	// 用來給控制元件設定寬高
	private android.widget.LinearLayout.LayoutParams mLayoutParams;
	
	public EditText getmEditTextInput() {
		return mEditTextInput;
	}

	public void setmEditTextInput(EditText mEditTextInput) {
		this.mEditTextInput = mEditTextInput;
	}

	public CommentPopupWindow(Context context,
			OnClickListener onClickListener,
			OnFocusChangeListener onFocusChangeListener) {
		super(context);
		mContext = context;
		mOnClickListener = onClickListener;
		mOnFocusChangeListener = onFocusChangeListener;
		initDate();
		initView();
		setPopupWindowSize();
		initEvent();
	}

	private void initDate() {
		// 獲取螢幕寬度和高度(以下注釋的獲取方式不能在該類構造裡不然執行報錯,所以換了種方式獲取)
		DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
		mScreenWidth = dm.widthPixels;
		mScreenHeight = dm.heightPixels;
	}

	private void initView() {
		//獲取佈局
		mPopupWindowView = View.inflate(mContext, R.layout.comment_dialog, null);
		//獲取控制元件
		mPopupWindowViewLayout = (RelativeLayout) mPopupWindowView.findViewById(R.id.commentPopupWindow_comment_layout);
		mCommentPopupWindowHead = (LinearLayout) mPopupWindowView.findViewById(R.id.commentPopupWindow_comment_head);
		mCommentPopupWindowFoot = (LinearLayout) mPopupWindowView.findViewById(R.id.commentPopupWindow_comment_foot);
		mTextViewCommentCount = (TextView) mPopupWindowView.findViewById(R.id.commentPopupWindow_comment_tv_commentsCount);
		mTextViewClose = (TextView) mPopupWindowView.findViewById(R.id.commentPopupWindow_comment_tv_close);
		mListViewComments = (ListView) mPopupWindowView.findViewById(R.id.commentPopupWindow_comment_lv);
		mEditTextInput = (EditText) mPopupWindowView.findViewById(R.id.commentPopupWindow_comment_et_input);
		mTextViewSend = (TextView) mPopupWindowView.findViewById(R.id.commentPopupWindow_comment_tv_send);
		mLayoutParams = (android.widget.LinearLayout.LayoutParams) mPopupWindowViewLayout.getLayoutParams();
	}

	private void setPopupWindowSize() {
		// 給ListView加上下外邊距——注意BUG:最大布局不能是相對佈局
		mCommentPopupWindowHead.measure(0, 0);
		int headHeight = mCommentPopupWindowHead.getMeasuredHeight();// 獲得頭部高度
		mCommentPopupWindowFoot.measure(0, 0);
		int footHeight = mCommentPopupWindowFoot.getMeasuredHeight();// 獲得尾部高度
		mListViewComments.setPadding(0, headHeight, 0, footHeight);
		this.setContentView(mPopupWindowView);
		// 視窗外也能點選(點選區域外可以關閉該視窗)
		this.setOutsideTouchable(true);
		// 窗體可點選
		this.setFocusable(true);
		// 解決低版本android的bug(點選"返回Back"也能使其消失,並且不會影響你的背景)
		this.setBackgroundDrawable(new BitmapDrawable());
		// 設定內容的佈局的高度(佔螢幕高度的百分比)
		mLayoutParams.height = (int) (mScreenHeight * mPopupWindowHeightForScreenPercent);
		mPopupWindowViewLayout.setLayoutParams(mLayoutParams);
		// 設定popupwindow寬度和螢幕寬度一致
		this.setWidth(mScreenWidth);
		// 設popupwindow高度與內容一樣高
		this.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
		// 這句話,讓pop覆蓋在輸入法上面
		this.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
		// 這句話,讓pop自適應輸入狀態
		this.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
	}

	private void initEvent() {
		mTextViewClose.setOnClickListener(mOnClickListener);
		mEditTextInput.setOnFocusChangeListener(mOnFocusChangeListener);
		mTextViewSend.setOnClickListener(mOnClickListener);
	}
	
	/**
	 * 根據輸入法鍵盤是否彈出,來改變評論popupWindow的佈局高度
	 * @param keyboardLayout
	 */
	public void setPopupWindowFroKeyboard(KeyboardLayout keyboardLayout ){
		if (keyboardLayout != null) {
			keyboardLayout.setKeyboardListener(new KeyboardLayoutListener() {

				@Override
				public void onKeyboardStateChanged(boolean isActive,int keyboardHeight) {
					if (isActive) { // 輸入法顯示時
						setpopupWindowHeight(keyboardHeight);
					} else {// 輸入法隱藏時
						setpopupWindowHeight(0);
					}
				}
			});
		}
	}
	// 設定評論視窗高度
	public void setpopupWindowHeight(int keyboardHeight) {
		mLayoutParams.height = (int) (mScreenHeight * mPopupWindowHeightForScreenPercent)- keyboardHeight;
		mPopupWindowViewLayout.setLayoutParams(mLayoutParams);
	}
}

三、我們的activity_main.xml佈局,com.t20.commentdemo.view.KeyboardLayoutKeyboardLayout.java的全路徑

<com.t20.commentdemo.view.KeyboardLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/keyboardLayout_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:onClick="openCommentWindow"
        android:textColor="#fff"
        android:text="點選彈出評論視窗" />

</com.t20.commentdemo.view.KeyboardLayout>

四、最後是活動MainActivity.java裡的CommentPopupWindow彈出窗呼叫

package com.t20.commentdemo;

import com.t20.commentdemo.view.CommentPopupWindow;
import com.t20.commentdemo.view.KeyboardLayout;

import android.os.Bundle;
import android.app.Activity;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;

public class MainActivity extends Activity {
	// 最大布局KeyboardLayout(RelativeLayout改寫)
	private KeyboardLayout mKeyboardLayoutRoot;
	// 評論
	private CommentPopupWindow mCommentPopupWindow;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		// 1、隱藏標題欄,在載入佈局之前設定(相容Android2.3.3版本)
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		// 2、隱藏狀態列
		//getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);								
		setContentView(R.layout.activity_main);
		//獲取控制元件
		mKeyboardLayoutRoot=(KeyboardLayout) findViewById(R.id.keyboardLayout_root);
	}

	/**
	 * 點選彈出評論視窗事件
	 * @param view
	 */
	public void openCommentWindow(View view) {
		//定義彈出窗
		mCommentPopupWindow=new CommentPopupWindow(MainActivity.this, onClickListener, onFocusChangeListener);
		// 顯示PopupWindow
		mCommentPopupWindow.showAtLocation(mKeyboardLayoutRoot, Gravity.BOTTOM,0, 0);
		// 讓輸入框失去焦點
		mCommentPopupWindow.getmEditTextInput().clearFocus();	
		// 根據輸入法鍵盤是否彈出,來改變評論popupWindow的佈局高度
		mCommentPopupWindow.setPopupWindowFroKeyboard(mKeyboardLayoutRoot);
	}
	/**
	 * 監聽評論中的點選事件
	 */
	private OnClickListener onClickListener=new OnClickListener() {
		
		@Override
		public void onClick(View view) {
			// TODO Auto-generated method stub
			switch (view.getId()) {
			// 關閉按鈕
			case R.id.commentPopupWindow_comment_tv_close:
				mCommentPopupWindow.dismiss();
				break;
			// 點擊發表評論
			case R.id.commentPopupWindow_comment_tv_send:
				break;
			}
		}
	};
	/**
	 * 監聽評論中的焦點改變事件
	 */
	private OnFocusChangeListener onFocusChangeListener=new OnFocusChangeListener() {
		
		@Override
		public void onFocusChange(View view, boolean hasFocus) {
			// TODO Auto-generated method stub
			switch (view.getId()) {
			case R.id.commentPopupWindow_comment_et_input:
				// 此處為得到焦點時的處理內容
				if (hasFocus) { 
					// 聚焦時,判斷使用者是否登入,沒登入可以跳轉到登入介面					
				}
				break;
			}
		}
	};
}