环信UI开源Demo情景分析九、聊天界面(一)

从会话列表点击一个会话进入聊天界面,现在咱们先来看看聊天界面的清单配置。(在代码中注释比较多,有兴趣的童雪不要只关注文本)

        <!-- 聊天页面 -->
        <activity
            android:name=".activity.ChatActivity"
            android:launchMode="singleTop"
            android:screenOrientation="portrait"
            android:theme="@style/horizontal_slide"
            android:windowSoftInputMode="adjustResize" >
        </activity>
adjustResize:该Activity总是调整屏幕的大小以便留出软键盘的空间。

界面布局相对复杂一点,其中在ListVIew上面还有一个语音提示的布局。

<RelativeLayout
            android:id="@+id/recording_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:background="@drawable/recording_hint_bg"
            android:padding="10dp"
            android:visibility="invisible" >

            <ImageView
                android:id="@+id/mic_image"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:src="@drawable/record_animate_01" />

            <TextView
                android:id="@+id/recording_hint"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/mic_image"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="5dp"
                android:padding="2dp"
                android:text="@string/move_up_to_cancel"
                android:textSize="10sp" />
        </RelativeLayout>
并且在输入框下面有一个ViewPager用来显示表情图标,与之同样的还有可以输入的图片,表情,名片等按钮。在这里对布局文件就不详细说明了。

/**
     * initView
     */
    protected void initView() {
        //录音布局
        recordingContainer = findViewById(R.id.recording_container);
        //录音图标
        micImage = (ImageView) findViewById(R.id.mic_image);
        //录音说明
        recordingHint = (TextView) findViewById(R.id.recording_hint);
        //聊天列表
        listView = (ListView) findViewById(R.id.list);
        //com.easemob.chatuidemo.widget.PasteEditText 输入框
        mEditTextContent = (PasteEditText) findViewById(R.id.et_sendmessage);
        //设置输入类型 当前为键盘图标
        buttonSetModeKeyboard = findViewById(R.id.btn_set_mode_keyboard);
        //底部布局
        edittext_layout = (RelativeLayout) findViewById(R.id.edittext_layout);
        //设置输入类型 当前为声音图标
        buttonSetModeVoice = findViewById(R.id.btn_set_mode_voice);
        //发送按钮
        buttonSend = findViewById(R.id.btn_send);
        //按下说话按钮
        buttonPressToSpeak = findViewById(R.id.btn_press_to_speak);
        //显示表情Viewpager
        expressionViewpager = (ViewPager) findViewById(R.id.vPager);
        //表情图片
        emojiIconContainer = (LinearLayout) findViewById(R.id.ll_face_container);
        //图片,表情,名片等按钮
        btnContainer = (LinearLayout) findViewById(R.id.ll_btn_container);
        //位置
        locationImgview = (ImageView) findViewById(R.id.btn_location);
        //emoji图标默认
        iv_emoticons_normal = (ImageView) findViewById(R.id.iv_emoticons_normal);
        //emoji图标 选择
        iv_emoticons_checked = (ImageView) findViewById(R.id.iv_emoticons_checked);
        //加载
        loadmorePB = (ProgressBar) findViewById(R.id.pb_load_more);
        //更多 加号图标
        btnMore = (Button) findViewById(R.id.btn_more);
        //设置默认图标状态
        iv_emoticons_normal.setVisibility(View.VISIBLE);
        iv_emoticons_checked.setVisibility(View.INVISIBLE);
        //更多  默认视图
        more = findViewById(R.id.more);
        //输入框默认背景
        edittext_layout.setBackgroundResource(R.drawable.input_bar_bg_normal);
        // 动画资源文件,用于录制语音时
        micImages = new Drawable[] { //
                getResources().getDrawable(R.drawable.record_animate_01), //
                getResources().getDrawable(R.drawable.record_animate_02), //
                getResources().getDrawable(R.drawable.record_animate_03), //
                getResources().getDrawable(R.drawable.record_animate_04), //
                getResources().getDrawable(R.drawable.record_animate_05), //
                getResources().getDrawable(R.drawable.record_animate_06), //
                getResources().getDrawable(R.drawable.record_animate_07), //
                getResources().getDrawable(R.drawable.record_animate_08), //
                getResources().getDrawable(R.drawable.record_animate_09), //
                getResources().getDrawable(R.drawable.record_animate_10), //
                getResources().getDrawable(R.drawable.record_animate_11), //
                getResources().getDrawable(R.drawable.record_animate_12), //
                getResources().getDrawable(R.drawable.record_animate_13), //
                getResources().getDrawable(R.drawable.record_animate_14), };
        // 表情list
        reslist = getExpressionRes(35);
        // 初始化表情viewpager
        List<View> views = new ArrayList<View>();
        View gv1 = getGridChildView(1);
        View gv2 = getGridChildView(2);
        views.add(gv1);
        views.add(gv2);
        expressionViewpager.setAdapter(new ExpressionPagerAdapter(views));
        edittext_layout.requestFocus();
        voiceRecorder = new VoiceRecorder(micImageHandler);
        buttonPressToSpeak.setOnTouchListener(new PressToSpeakListen());
        mEditTextContent.setOnFocusChangeListener(new OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    edittext_layout.setBackgroundResource(R.drawable.input_bar_bg_active);
                } else {
                    edittext_layout.setBackgroundResource(R.drawable.input_bar_bg_normal);
                }
            }
        });
        mEditTextContent.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                edittext_layout.setBackgroundResource(R.drawable.input_bar_bg_active);
                more.setVisibility(View.GONE);
                iv_emoticons_normal.setVisibility(View.VISIBLE);
                iv_emoticons_checked.setVisibility(View.INVISIBLE);
                emojiIconContainer.setVisibility(View.GONE);
                btnContainer.setVisibility(View.GONE);
            }
        });
        // 监听文字框
        mEditTextContent.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                if (!TextUtils.isEmpty(s)) {
                    btnMore.setVisibility(View.GONE);
                    buttonSend.setVisibility(View.VISIBLE);
                } else {
                    btnMore.setVisibility(View.VISIBLE);
                    buttonSend.setVisibility(View.GONE);
                }
            }
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            @Override
            public void afterTextChanged(Editable s) {
            }
        });
    }
在这个初始化方法中,先赋值了一些控件按钮,加载录音时候的动画图片。将表情加载到ViewPager中。
并且动态监听输入框的变换,及时更改表情,发送等控件的状态。

    private void setUpView() {
        //表情点击事件
        iv_emoticons_normal.setOnClickListener(this);
        iv_emoticons_checked.setOnClickListener(this);
        // position = getIntent().getIntExtra("position", -1);
        //剪切板
        clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
        manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        //隐藏键盘
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
        //屏幕睡眠时候关闭软键盘
        wakeLock = ((PowerManager) getSystemService(Context.POWER_SERVICE)).newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "demo");
        // 判断单聊还是群聊
        chatType = getIntent().getIntExtra("chatType", CHATTYPE_SINGLE);
        if (chatType == CHATTYPE_SINGLE) { // 单聊
            toChatUsername = getIntent().getStringExtra("userId");
            ((TextView) findViewById(R.id.name)).setText(toChatUsername);
            // conversation =
            // EMChatManager.getInstance().getConversation(toChatUsername,false);
        } else {
            // 群聊
            findViewById(R.id.container_to_group).setVisibility(View.VISIBLE);
            //删除聊天记录
            findViewById(R.id.container_remove).setVisibility(View.GONE);
            //声音
            findViewById(R.id.container_voice_call).setVisibility(View.GONE);
            //视频
            findViewById(R.id.container_video_call).setVisibility(View.GONE);
            toChatUsername = getIntent().getStringExtra("groupId");
            group = EMGroupManager.getInstance().getGroup(toChatUsername);
            //设置Group name
            if (group != null)
                ((TextView) findViewById(R.id.name)).setText(group.getGroupName());
            else
                ((TextView) findViewById(R.id.name)).setText(toChatUsername);
            // conversation =
            // EMChatManager.getInstance().getConversation(toChatUsername,true);
        }
        conversation = EMChatManager.getInstance().getConversation(toChatUsername);
        // 把此会话的未读数置为0
        conversation.resetUnreadMsgCount();
        //加载消息
        // 初始化db时,每个conversation加载数目是getChatOptions().getNumberOfMessagesLoaded
        // 这个数目如果比用户期望进入会话界面时显示的个数不一样,就多加载一些
        final List<EMMessage> msgs = conversation.getAllMessages();
        int msgCount = msgs != null ? msgs.size() : 0;
        //pagesize = 20
        if (msgCount < conversation.getAllMsgCount() && msgCount < pagesize) {
            String msgId = null;
            if (msgs != null && msgs.size() > 0) {
                msgId = msgs.get(0).getMsgId();
            }
            if (chatType == CHATTYPE_SINGLE) {
                conversation.loadMoreMsgFromDB(msgId, pagesize);
            } else {
                conversation.loadMoreGroupMsgFromDB(msgId, pagesize);
            }
        }
        adapter = new MessageAdapter(this, toChatUsername, chatType);
        // 显示消息 并设置listview的一些事件
        listView.setAdapter(adapter);
        listView.setOnScrollListener(new ListScrollListener());
        adapter.refreshSelectLast();
        listView.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                //当触摸listview区域时,隐藏键盘、表情
                hideKeyboard();
                more.setVisibility(View.GONE);
                iv_emoticons_normal.setVisibility(View.VISIBLE);
                iv_emoticons_checked.setVisibility(View.INVISIBLE);
                emojiIconContainer.setVisibility(View.GONE);
                btnContainer.setVisibility(View.GONE);
                return false;
            }
        });
        // 监听当前会话的群聊解散被T事件
        groupListener = new GroupListener();
        EMGroupManager.getInstance().addGroupChangeListener(groupListener);
        // show forward message if the message is not null
        String forward_msg_id = getIntent().getStringExtra("forward_msg_id");
        if (forward_msg_id != null) {
            // 显示发送要转发的消息
            forwardMessage(forward_msg_id);
        }
    }

该方法主要是对聊天内容区的一些设置,通过对聊天类型的判断从而设置Name,在聊天区域被触摸的时候更改输入栏。监听群T事件以及获得转发内容。

接下来看一下点击事件。

/**
	 * 消息图标点击事件
	 * 
	 * @param view
	 */
	@Override
	public void onClick(View view) {
		String st1 = getResources().getString(R.string.not_connect_to_server);
		int id = view.getId();
		if (id == R.id.btn_send) {// 点击发送按钮(发文字和表情)
			String s = mEditTextContent.getText().toString();
			sendText(s);
		} else if (id == R.id.btn_take_picture) {
			selectPicFromCamera();// 点击照相图标
		} else if (id == R.id.btn_picture) {
			selectPicFromLocal(); // 点击图片图标
		} else if (id == R.id.btn_location) { // 位置
			startActivityForResult(new Intent(this, BaiduMapActivity.class), REQUEST_CODE_MAP);
		} else if (id == R.id.iv_emoticons_normal) { // 点击显示表情框
			more.setVisibility(View.VISIBLE);
			iv_emoticons_normal.setVisibility(View.INVISIBLE);
			iv_emoticons_checked.setVisibility(View.VISIBLE);
			btnContainer.setVisibility(View.GONE);
			emojiIconContainer.setVisibility(View.VISIBLE);
			hideKeyboard();
		} else if (id == R.id.iv_emoticons_checked) { // 点击隐藏表情框
			iv_emoticons_normal.setVisibility(View.VISIBLE);
			iv_emoticons_checked.setVisibility(View.INVISIBLE);
			btnContainer.setVisibility(View.VISIBLE);
			emojiIconContainer.setVisibility(View.GONE);
			more.setVisibility(View.GONE);

		} else if (id == R.id.btn_video) {
			// 点击摄像图标
			Intent intent = new Intent(ChatActivity.this, ImageGridActivity.class);
			startActivityForResult(intent, REQUEST_CODE_SELECT_VIDEO);
		} else if (id == R.id.btn_file) { // 点击文件图标
			selectFileFromLocal();
		} else if (id == R.id.btn_voice_call) { // 点击语音电话图标
			if (!EMChatManager.getInstance().isConnected())
				Toast.makeText(this, st1, 0).show();
			else
				startActivity(new Intent(ChatActivity.this, VoiceCallActivity.class).putExtra("username", toChatUsername).putExtra("isComingCall", false));
		} else if (id == R.id.btn_video_call) { // 视频通话
			if (!EMChatManager.getInstance().isConnected())
				Toast.makeText(this, st1, 0).show();
			else
				startActivity(new Intent(this, VideoCallActivity.class).putExtra("username", toChatUsername).putExtra("isComingCall", false));
		}
	}
点击发送按钮(发文字和表情)sendText:

	/**
	 * 发送文本消息
	 * 
	 * @param content
	 *            message content
	 * @param isResend
	 *            boolean resend
	 */
	private void sendText(String content) {

		if (content.length() > 0) {
			EMMessage message = EMMessage.createSendMessage(EMMessage.Type.TXT);
			// 如果是群聊,设置chattype,默认是单聊
			if (chatType == CHATTYPE_GROUP)
				message.setChatType(ChatType.GroupChat);
			TextMessageBody txtBody = new TextMessageBody(content);
			// 设置消息body
			message.addBody(txtBody);
			// 设置要发给谁,用户username或者群聊groupid
			message.setReceipt(toChatUsername);
			// 把messgage加到conversation中
			conversation.addMessage(message);
			// 通知adapter有消息变动,adapter会根据加入的这条message显示消息和调用sdk的发送方法
			adapter.refreshSelectLast();
			mEditTextContent.setText("");
			setResult(RESULT_OK);
		}
	}
通过SDK发送消息,将消息加入到EMMessage中,发送之后刷新ListView,并且重置输入框。
照相selectPicFromCamera:

/**
	 * 照相获取图片
	 */
	public void selectPicFromCamera() {
		if (!CommonUtils.isExitsSdcard()) {
			String st = getResources().getString(R.string.sd_card_does_not_exist);
			Toast.makeText(getApplicationContext(), st, 0).show();
			return;
		}

		cameraFile = new File(PathUtil.getInstance().getImagePath(), DemoApplication.getInstance().getUserName() + System.currentTimeMillis() + ".jpg");
		cameraFile.getParentFile().mkdirs();
		startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE).putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cameraFile)), REQUEST_CODE_CAMERA);
	}
首先判断本地是否有存储卡,格式话生成的图片名称,最后调用系统相机进行拍照。

发送本地图片selectPicFromLocal:

/**
	 * 从图库获取图片
	 */
	public void selectPicFromLocal() {
		Intent intent;
		if (Build.VERSION.SDK_INT < 19) {
			intent = new Intent(Intent.ACTION_GET_CONTENT);
			intent.setType("image/*");

		} else {
			intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
		}
		startActivityForResult(intent, REQUEST_CODE_LOCAL);
	}
首先判断系统版本,如果小于19的话查找image下图片,否则直接通过系统提供的provider进行查找。

当点击位置的时候,调用百度地图的SDK(此处不详细说明)。

点击表情按钮的时候,重置more将其更改为表情布局,并且隐藏键盘。

more.setVisibility(View.VISIBLE);
			iv_emoticons_normal.setVisibility(View.INVISIBLE);
			iv_emoticons_checked.setVisibility(View.VISIBLE);
			btnContainer.setVisibility(View.GONE);
			emojiIconContainer.setVisibility(View.VISIBLE);
			hideKeyboard();
当继续点击表情按钮时,隐藏表情栏:

iv_emoticons_normal.setVisibility(View.VISIBLE);
			iv_emoticons_checked.setVisibility(View.INVISIBLE);
			btnContainer.setVisibility(View.VISIBLE);
			emojiIconContainer.setVisibility(View.GONE);
			more.setVisibility(View.GONE);
点击文件按钮selectFileFromLocal:

	/**
	 * 选择文件
	 */
	private void selectFileFromLocal() {
		Intent intent = null;
		if (Build.VERSION.SDK_INT < 19) {
			intent = new Intent(Intent.ACTION_GET_CONTENT);
			intent.setType("*/*");
			intent.addCategory(Intent.CATEGORY_OPENABLE);

		} else {
			intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
		}
		startActivityForResult(intent, REQUEST_CODE_SELECT_FILE);
	}
同发送照片。

当点击摄像图标的时候,启动一个界面ImageGridActivity:

public class ImageGridActivity extends FragmentActivity {
	private static final String TAG = "ImageGridActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
//        if (BuildConfig.DEBUG) {
//            Utils.enableStrictMode();
//        }
        super.onCreate(savedInstanceState);

        if (getSupportFragmentManager().findFragmentByTag(TAG) == null) {
            final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            ft.add(android.R.id.content, new ImageGridFragment(), TAG);
            ft.commit();
        }
    }
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
	}
}
  <!-- choose video -->
        <activity
            android:name=".activity.ImageGridActivity"
            android:screenOrientation="portrait"
            android:theme="@style/horizontal_slide"
            android:windowSoftInputMode="stateAlwaysHidden" >
        </activity>
其中内容是用ImageGridFragment来显示的。可以看到的网格界面使用GridView来显示的:

<GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridView"
    android:drawSelectorOnTop="true"
    android:listSelector="@drawable/photogrid_list_selector"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:columnWidth="@dimen/image_thumbnail_size"
    android:horizontalSpacing="@dimen/image_thumbnail_spacing"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth"
    android:verticalSpacing="@dimen/image_thumbnail_spacing" >
</GridView>

接下来我们来看一下ImageGridFragment里面到底是怎么设置的。

/**
	 * Empty constructor as per the Fragment documentation
	 */
	public ImageGridFragment() {
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		mImageThumbSize = getResources().getDimensionPixelSize(R.dimen.image_thumbnail_size);
		mImageThumbSpacing = getResources().getDimensionPixelSize(R.dimen.image_thumbnail_spacing);
		mList = new ArrayList<VideoEntity>();
		getVideoFile();
		mAdapter = new ImageAdapter(getActivity());
		ImageCache.ImageCacheParams cacheParams = new ImageCache.ImageCacheParams();
		cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 25% of
													// app memory

		// The ImageFetcher takes care of loading images into our ImageView
		// children asynchronously
		mImageResizer = new ImageResizer(getActivity(), mImageThumbSize);
		mImageResizer.setLoadingImage(R.drawable.empty_photo);
		mImageResizer.addImageCache(getActivity().getSupportFragmentManager(), cacheParams);

	}

首先是一个空的构造方法,接下来在onCreate方法中获取设置的布局尺寸,其中有一点需要注意下:

getDimension和getDimensionPixelOffset的功能类似,

都是获取某个dimen的值,但是如果单位是dp或sp,则需要将其乘以density

如果是px,则不乘。并且getDimension返回float,getDimensionPixelOffset返回int.

而getDimensionPixelSize则不管写的是dp还是sp还是px,都会乘以denstiy.

然后定义了一个数组,类型为VideoEntity.

public class VideoEntity {
	public int ID;
	public String title;
	public String filePath;
	public int size;
	public int duration;
}
这是一个视频描述类,接下来调用方法获取文件:

	private void getVideoFile() {
		ContentResolver mContentResolver = getActivity().getContentResolver();
		Cursor cursor = mContentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Video.DEFAULT_SORT_ORDER);
		if (cursor != null && cursor.moveToFirst()) {
			do {
				// ID:MediaStore.Audio.Media._ID
				int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID));
				// 名称:MediaStore.Audio.Media.TITLE
				String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.TITLE));
				// 路径:MediaStore.Audio.Media.DATA
				String url = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA));
				// 总播放时长:MediaStore.Audio.Media.DURATION
				int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION));
				// 大小:MediaStore.Audio.Media.SIZE
				int size = (int) cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE));
				VideoEntity entty = new VideoEntity();
				entty.ID = id;
				entty.title = title;
				entty.filePath = url;
				entty.duration = duration;
				entty.size = size;
				mList.add(entty);
			} while (cursor.moveToNext());
		}
		if (cursor != null) {
			cursor.close();
			cursor = null;
		}
	}
此处的查询是查询系统里面自动记录的文件信息。

当获取到内容后通过Adapter显示出来:

private class ImageAdapter extends BaseAdapter {
		private final Context mContext;
		private int mItemHeight = 0;
		private RelativeLayout.LayoutParams mImageViewLayoutParams;
		public ImageAdapter(Context context) {
			super();
			mContext = context;
			mImageViewLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
		}
		@Override
		public int getCount() {
			return mList.size() + 1;
		}
		@Override
		public Object getItem(int position) {
			return (position == 0) ? null : mList.get(position - 1);
		}
		@Override
		public long getItemId(int position) {
			return position;
		}
		@Override
		public View getView(int position, View convertView, ViewGroup container) {
			ViewHolder holder = null;
			if (convertView == null) {
				holder = new ViewHolder();
				convertView = LayoutInflater.from(mContext).inflate(R.layout.choose_griditem, container, false);
				holder.imageView = (RecyclingImageView) convertView.findViewById(R.id.imageView);
				holder.icon = (ImageView) convertView.findViewById(R.id.video_icon);
				holder.tvDur = (TextView) convertView.findViewById(R.id.chatting_length_iv);
				holder.tvSize = (TextView) convertView.findViewById(R.id.chatting_size_iv);
				holder.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
				holder.imageView.setLayoutParams(mImageViewLayoutParams);
				convertView.setTag(holder);
			} else {
				holder = (ViewHolder) convertView.getTag();
			}
			// Check the height matches our calculated column width
			if (holder.imageView.getLayoutParams().height != mItemHeight) {
				holder.imageView.setLayoutParams(mImageViewLayoutParams);
			}
			// Finally load the image asynchronously into the ImageView, this
			// also takes care of
			// setting a placeholder image while the background thread runs
			String st1 = getResources().getString(R.string.Video_footage);
			if (position == 0) {
				holder.icon.setVisibility(View.GONE);
				holder.tvDur.setVisibility(View.GONE);
				holder.tvSize.setText(st1);
				holder.imageView.setImageResource(R.drawable.actionbar_camera_icon);
			} else {
				holder.icon.setVisibility(View.VISIBLE);
				VideoEntity entty = mList.get(position - 1);
				holder.tvDur.setVisibility(View.VISIBLE);

				holder.tvDur.setText(DateUtils.toTime(entty.duration));
				holder.tvSize.setText(TextFormater.getDataSize(entty.size));
				holder.imageView.setImageResource(R.drawable.empty_photo);
				mImageResizer.loadImage(entty.filePath, holder.imageView);
			}
			return convertView;
			// END_INCLUDE(load_gridview_item)
		}
		/**
		 * Sets the item height. Useful for when we know the column width so the
		 * height can be set to match.
		 * 
		 * @param height
		 */
		public void setItemHeight(int height) {
			if (height == mItemHeight) {
				return;
			}
			mItemHeight = height;
			mImageViewLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, mItemHeight);
			mImageResizer.setImageSize(height);
			notifyDataSetChanged();
		}
		class ViewHolder {
			RecyclingImageView imageView;
			ImageView icon;
			TextView tvDur;
			TextView tvSize;
		}
	}
其中mList为全局变量,由于显示的视图里面第一项是拍摄视频,所以count要+1,getItem中position需要-1。接下来设置缓存。是通过自己写的缓存类在设置的:

/**
	 * A holder class that contains cache parameters.
	 */
	public static class ImageCacheParams {
		public int memCacheSize = DEFAULT_MEM_CACHE_SIZE;
		public int compressQuality = DEFAULT_COMPRESS_QUALITY;
		public boolean memoryCacheEnabled = DEFAULT_MEM_CACHE_ENABLED;
		public boolean initDiskCacheOnCreate = DEFAULT_INIT_DISK_CACHE_ON_CREATE;

		 
		/**
		 * Sets the memory cache size based on a percentage of the max available
		 * VM memory. Eg. setting percent to 0.2 would set the memory cache to
		 * one fifth of the available memory. Throws
		 * {@link IllegalArgumentException} if percent is < 0.01 or > .8.
		 * memCacheSize is stored in kilobytes instead of bytes as this will
		 * eventually be passed to construct a LruCache which takes an int in
		 * its constructor.
		 * 
		 * This value should be chosen carefully based on a number of factors
		 * Refer to the corresponding Android Training class for more
		 * discussion: http://developer.android.com/training/displaying-bitmaps/
		 * 
		 * @param percent
		 *            Percent of available app memory to use to size memory
		 *            cache
		 */
		public void setMemCacheSizePercent(float percent) {
			if (percent < 0.01f || percent > 0.8f) {
				throw new IllegalArgumentException(
						"setMemCacheSizePercent - percent must be "
								+ "between 0.01 and 0.8 (inclusive)");
			}
			memCacheSize = Math.round(percent
					* Runtime.getRuntime().maxMemory() / 1024);
		}
	}

在onCreate方法中获取到数据,接下来在onCreateView中设置布局。

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		final View v = inflater.inflate(R.layout.image_grid_fragment, container, false);
		final GridView mGridView = (GridView) v.findViewById(R.id.gridView);
		mGridView.setAdapter(mAdapter);
		mGridView.setOnItemClickListener(this);
		mGridView.setOnScrollListener(new AbsListView.OnScrollListener() {
			@Override
			public void onScrollStateChanged(AbsListView absListView, int scrollState) {
				// Pause fetcher to ensure smoother scrolling when flinging
				if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
					// Before Honeycomb pause image loading on scroll to help
					// with performance
					if (!Utils.hasHoneycomb()) {
						mImageResizer.setPauseWork(true);
					}
				} else {
					mImageResizer.setPauseWork(false);
				}
			}

			@Override
			public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
			}
		});

		// This listener is used to get the final width of the GridView and then
		// calculate the
		// number of columns and the width of each column. The width of each
		// column is variable
		// as the GridView has stretchMode=columnWidth. The column width is used
		// to set the height
		// of each view so we get nice square thumbnails.
		mGridView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
			@TargetApi(VERSION_CODES.JELLY_BEAN)
			@Override
			public void onGlobalLayout() {
				final int numColumns = (int) Math.floor(mGridView.getWidth() / (mImageThumbSize + mImageThumbSpacing));
				if (numColumns > 0) {
					final int columnWidth = (mGridView.getWidth() / numColumns) - mImageThumbSpacing;
					mAdapter.setItemHeight(columnWidth);
					if (BuildConfig.DEBUG) {
						Log.d(TAG, "onCreateView - numColumns set to " + numColumns);
					}
					if (Utils.hasJellyBean()) {
						mGridView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
					} else {
						mGridView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
					}
				}
			}
		});
		return v;
	}

其中通过暂停显示视频来确保平滑滚动。

有时候需要在onCreate方法中知道某个View组件的宽度和高度等信息,而直接调用View组件的getWidth()、getHeight()、getMeasuredWidth()、getMeasuredHeight()、getTop()、getLeft()等方法是无法获取到真实值的,只会得到0。这是因为View组件布局要在onResume回调后完成。下面提供实现方法,onGlobalLayout回调会在view布局完成时自动调用。

点击事件:

@Override
	public void onItemClick(AdapterView<?> parent, View v, final int position, long id) {
		mImageResizer.setPauseWork(true);
		if (position == 0) {
			Intent intent = new Intent();
			intent.setClass(getActivity(), RecorderVideoActivity.class);
			startActivityForResult(intent, 100);
		} else {
			VideoEntity vEntty = mList.get(position - 1);
			// 限制大小不能超过10M
			if (vEntty.size > 1024 * 1024 * 10) {
				String st = getResources().getString(R.string.temporary_does_not);
				Toast.makeText(getActivity(), st, Toast.LENGTH_SHORT).show();
				return;
			}
			Intent intent = getActivity().getIntent().putExtra("path", vEntty.filePath).putExtra("dur", vEntty.duration);
			getActivity().setResult(Activity.RESULT_OK, intent);
			getActivity().finish();
		}
	}
如果点击第一个,则开始录像,点击其他的视频则将视频的路径还有时长传递过去。

其中还有一个RecorderVideoActivity用来录像,此功能在这里不详细分析,有兴趣的同学可以自己去学习以下。

@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		if (resultCode == Activity.RESULT_OK) {
			if (requestCode == 100) {
				Uri uri = data.getParcelableExtra("uri");
				String[] projects = new String[] { MediaStore.Video.Media.DATA, MediaStore.Video.Media.DURATION };
				Cursor cursor = getActivity().getContentResolver().query(uri, projects, null, null, null);
				int duration = 0;
				String filePath = null;

				if (cursor.moveToFirst()) {
					// 路径:MediaStore.Audio.Media.DATA
					filePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA));
					// 总播放时长:MediaStore.Audio.Media.DURATION
					duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION));
					System.out.println("duration:" + duration);
				}
				if (cursor != null) {
					cursor.close();
					cursor = null;
				}
				getActivity().setResult(Activity.RESULT_OK, getActivity().getIntent().putExtra("path", filePath).putExtra("dur", duration));
				getActivity().finish();
			}
		}
	}

录像之后返回此界面,获取地址和时长后返回聊天界面。

	@Override
	public void onResume() {
		super.onResume();
		mImageResizer.setExitTasksEarly(false);
		mAdapter.notifyDataSetChanged();
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		mImageResizer.closeCache();
		mImageResizer.clearCache();
	}

还有两个发方法对数据更新及清除缓存。

再次回到聊天界面后调用onActivityResult方法:

/**
	 * onActivityResult
	 */
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		if (resultCode == RESULT_CODE_EXIT_GROUP) {
			setResult(RESULT_OK);
			finish();
			return;
		}
		if (requestCode == REQUEST_CODE_CONTEXT_MENU) {
			switch (resultCode) {
			case RESULT_CODE_COPY: // 复制消息
				EMMessage copyMsg = ((EMMessage) adapter.getItem(data.getIntExtra("position", -1)));
				// clipboard.setText(SmileUtils.getSmiledText(ChatActivity.this,
				// ((TextMessageBody) copyMsg.getBody()).getMessage()));
				clipboard.setText(((TextMessageBody) copyMsg.getBody()).getMessage());
				break;
			case RESULT_CODE_DELETE: // 删除消息
				EMMessage deleteMsg = (EMMessage) adapter.getItem(data.getIntExtra("position", -1));
				conversation.removeMessage(deleteMsg.getMsgId());
				adapter.refreshSeekTo(data.getIntExtra("position", adapter.getCount()) - 1);
				break;

			case RESULT_CODE_FORWARD: // 转发消息
				EMMessage forwardMsg = (EMMessage) adapter.getItem(data.getIntExtra("position", 0));
				Intent intent = new Intent(this, ForwardMessageActivity.class);
				intent.putExtra("forward_msg_id", forwardMsg.getMsgId());
				startActivity(intent);

				break;

			default:
				break;
			}
		}
		if (resultCode == RESULT_OK) { // 清空消息
			if (requestCode == REQUEST_CODE_EMPTY_HISTORY) {
				// 清空会话
				EMChatManager.getInstance().clearConversation(toChatUsername);
				adapter.refresh();
			} else if (requestCode == REQUEST_CODE_CAMERA) { // 发送照片
				if (cameraFile != null && cameraFile.exists())
					sendPicture(cameraFile.getAbsolutePath());
			} else if (requestCode == REQUEST_CODE_SELECT_VIDEO) { // 发送本地选择的视频

				int duration = data.getIntExtra("dur", 0);
				String videoPath = data.getStringExtra("path");
				File file = new File(PathUtil.getInstance().getImagePath(), "thvideo" + System.currentTimeMillis());
				Bitmap bitmap = null;
				FileOutputStream fos = null;
				try {
					if (!file.getParentFile().exists()) {
						file.getParentFile().mkdirs();
					}
					bitmap = ThumbnailUtils.createVideoThumbnail(videoPath, 3);
					if (bitmap == null) {
						EMLog.d("chatactivity", "problem load video thumbnail bitmap,use default icon");
						bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.app_panel_video_icon);
					}
					fos = new FileOutputStream(file);

					bitmap.compress(CompressFormat.JPEG, 100, fos);

				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					if (fos != null) {
						try {
							fos.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
						fos = null;
					}
					if (bitmap != null) {
						bitmap.recycle();
						bitmap = null;
					}

				}
				sendVideo(videoPath, file.getAbsolutePath(), duration / 1000);

			} else if (requestCode == REQUEST_CODE_LOCAL) { // 发送本地图片
				if (data != null) {
					Uri selectedImage = data.getData();
					if (selectedImage != null) {
						sendPicByUri(selectedImage);
					}
				}
			} else if (requestCode == REQUEST_CODE_SELECT_FILE) { // 发送选择的文件
				if (data != null) {
					Uri uri = data.getData();
					if (uri != null) {
						sendFile(uri);
					}
				}

			} else if (requestCode == REQUEST_CODE_MAP) { // 地图
				double latitude = data.getDoubleExtra("latitude", 0);
				double longitude = data.getDoubleExtra("longitude", 0);
				String locationAddress = data.getStringExtra("address");
				if (locationAddress != null && !locationAddress.equals("")) {
					more(more);
					sendLocationMsg(latitude, longitude, "", locationAddress);
				} else {
					String st = getResources().getString(R.string.unable_to_get_loaction);
					Toast.makeText(this, st, 0).show();
				}
				// 重发消息
			} else if (requestCode == REQUEST_CODE_TEXT || requestCode == REQUEST_CODE_VOICE || requestCode == REQUEST_CODE_PICTURE || requestCode == REQUEST_CODE_LOCATION || requestCode == REQUEST_CODE_VIDEO || requestCode == REQUEST_CODE_FILE) {
				resendMessage();
			} else if (requestCode == REQUEST_CODE_COPY_AND_PASTE) {
				// 粘贴
				if (!TextUtils.isEmpty(clipboard.getText())) {
					String pasteText = clipboard.getText().toString();
					if (pasteText.startsWith(COPY_IMAGE)) {
						// 把图片前缀去掉,还原成正常的path
						sendPicture(pasteText.replace(COPY_IMAGE, ""));
					}

				}
			} else if (requestCode == REQUEST_CODE_ADD_TO_BLACKLIST) { // 移入黑名单
				EMMessage deleteMsg = (EMMessage) adapter.getItem(data.getIntExtra("position", -1));
				addUserToBlacklist(deleteMsg.getFrom());
			} else if (conversation.getMsgCount() > 0) {
				adapter.refresh();
				setResult(RESULT_OK);
			} else if (requestCode == REQUEST_CODE_GROUP_DETAIL) {
				adapter.refresh();
			}
		}
	}

首先是退出组的消息,收到消息后直接退出该聊天界面。

接下来是上下文菜单的消息,其中转发消息将进入ForwardMessageActivity界面,选择联系人进行转发消息。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值