entry : emoticons.entrySet()) {
+ Matcher matcher = entry.getKey().matcher(key);
+ if (matcher.find()) {
+ b = true;
+ break;
+ }
+ }
+
+ return b;
+ }
+
+}
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/utils/StatusBarUtils.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/utils/StatusBarUtils.java
new file mode 100644
index 0000000..5c5a88a
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/utils/StatusBarUtils.java
@@ -0,0 +1,140 @@
+package com.shunzhi.mychartlibrary.utils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Build;
+import android.support.annotation.ColorInt;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+import java.lang.reflect.Field;
+
+
+/**
+ * Created by Horrarndoo on 2017/8/31.
+ *
+ * StatusBar工具类
+ */
+public class StatusBarUtils {
+
+ private static final int DEFAULT_STATUS_BAR_ALPHA = 0;
+
+ /**
+ * 设置状态栏颜色
+ *
+ * @param activity 需要设置的 activity
+ * @param color 状态栏颜色值
+ */
+ public static void setColor(Activity activity, @ColorInt int color) {
+ setBarColor(activity, color);
+ }
+
+ /**
+ * 设置状态栏背景色
+ * 4.4以下不处理
+ * 4.4使用默认沉浸式状态栏
+ *
+ * @param color 要为状态栏设置的颜色值
+ */
+ public static void setBarColor(Activity activity, int color) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ Window win = activity.getWindow();
+ View decorView = win.getDecorView();
+ win.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//沉浸式状态栏(4.4-5.0透明,5.0以上半透明)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//android5.0以上设置透明效果
+ win.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//清除flag,为了android5.0以上也全透明效果
+ //让应用的主体内容占用系统状态栏的空间
+// int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+// | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
+ int option = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
+ decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() | option);
+ win.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ win.setStatusBarColor(color);//设置状态栏背景色
+ }
+ }
+ }
+
+ /**
+ * 设置状态栏全透明
+ *
+ * @param activity 需要设置的activity
+ */
+ public static void setTransparent(Activity activity) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ return;
+ }
+ setColor(activity, Color.TRANSPARENT);
+ }
+
+ /**
+ * 修正 Toolbar 的位置
+ * 在 Android 4.4 版本下无法显示内容在 StatusBar 下,所以无需修正 Toolbar 的位置
+ *
+ * @param toolbar
+ */
+ public static void fixToolbar(Toolbar toolbar, Activity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ int statusHeight = getStatusBarHeight(activity);
+ ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) toolbar.getLayoutParams();
+ layoutParams.setMargins(0, statusHeight, 0, 0);
+ }
+ }
+
+ /**
+ * 获取系统状态栏高度
+ *
+ * @param context
+ * @return
+ */
+ public static int getStatusBarHeight(Context context) {
+ Class> c = null;
+ Object obj = null;
+ Field field = null;
+ int x = 0, statusBarHeight = 0;
+ try {
+ c = Class.forName("com.android.internal.R$dimen");
+ obj = c.newInstance();
+ field = c.getField("status_bar_height");
+ x = Integer.parseInt(field.get(obj).toString());
+ statusBarHeight = context.getResources().getDimensionPixelSize(x);
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ return statusBarHeight;
+ }
+
+// /**
+// * 获取状态栏高度
+// *
+// * @param context context
+// * @return 状态栏高度
+// */
+// private static int getStatusBarHeight(Context context) {
+// // 获得状态栏高度
+// int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen",
+// "android");
+// return context.getResources().getDimensionPixelSize(resourceId);
+// }
+
+ /**
+ * 计算状态栏颜色
+ *
+ * @param color color值
+ * @param alpha alpha值
+ * @return 最终的状态栏颜色
+ */
+ private static int calculateStatusColor(@ColorInt int color, int alpha) {
+ float a = 1 - alpha / 255f;
+ int red = color >> 16 & 0xff;
+ int green = color >> 8 & 0xff;
+ int blue = color & 0xff;
+ red = (int) (red * a + 0.5);
+ green = (int) (green * a + 0.5);
+ blue = (int) (blue * a + 0.5);
+ return 0xff << 24 | red << 16 | green << 8 | blue;
+ }
+}
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/utils/ThreadPoolUtils.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/utils/ThreadPoolUtils.java
new file mode 100644
index 0000000..6af797c
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/utils/ThreadPoolUtils.java
@@ -0,0 +1,57 @@
+package com.shunzhi.mychartlibrary.utils;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Created by Mao Jiqing on 2016/11/7.
+ */
+
+public class ThreadPoolUtils {
+ //线程池核心线程数
+ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
+ private static int CORE_POOL_SIZE = CPU_COUNT + 1;
+ //线程池最大线程数
+ private static int MAX_POOL_SIZE = CPU_COUNT * 2 + 1;
+ //额外线程空状态生存时间
+ private static int KEEP_ALIVE_TIME = 10000;
+ //阻塞队列。当核心线程都被占用,且阻塞队列已满的情况下,才会开启额外线程。
+ private static BlockingQueue workQueue = new ArrayBlockingQueue(10);
+ //线程池
+ private static ThreadPoolExecutor threadPool;
+
+ private ThreadPoolUtils() {
+ }
+
+ //线程工厂
+ private static ThreadFactory threadFactory = new ThreadFactory() {
+ private final AtomicInteger integer = new AtomicInteger();
+
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "myThreadPool thread:" + integer.getAndIncrement());
+ }
+ };
+
+ static {
+ threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME,
+ TimeUnit.SECONDS, workQueue, threadFactory);
+ }
+
+ public static void execute(Runnable runnable) {
+ threadPool.execute(runnable);
+ }
+
+ public static void execute(FutureTask futureTask) {
+ threadPool.execute(futureTask);
+ }
+
+ public static void cancel(FutureTask futureTask) {
+ futureTask.cancel(true);
+ }
+}
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/AudioRecordButton.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/AudioRecordButton.java
new file mode 100644
index 0000000..de4bbc8
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/AudioRecordButton.java
@@ -0,0 +1,336 @@
+package com.shunzhi.mychartlibrary.widget;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.Button;
+import android.widget.Toast;
+
+
+import com.shunzhi.mychartlibrary.R;
+import com.shunzhi.mychartlibrary.utils.AudioManager;
+import com.shunzhi.mychartlibrary.utils.FileSaveUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+
+public class AudioRecordButton extends Button implements AudioManager.AudioStageListener {
+ private static final int STATE_NORMAL = 1;
+ private static final int STATE_RECORDING = 2;
+ private static final int STATE_WANT_TO_CANCEL = 3;
+ private static final int DISTANCE_Y_CANCEL = 50;
+ private static final int OVERTIME = 60;
+ private int mCurrentState = STATE_NORMAL;
+ // 已经开始录音
+ private boolean isRecording = false;
+ private DialogManager mDialogManager;
+ private float mTime = 0;
+ // 是否触发了onlongclick,准备好了
+ private boolean mReady;
+ private AudioManager mAudioManager;
+ private String saveDir = FileSaveUtil.voice_dir;
+
+ private Handler mp3handler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ // TODO Auto-generated method stub
+ switch (msg.what) {
+ case AudioManager.MSG_ERROR_AUDIO_RECORD:
+ Toast.makeText(getContext(), "录音权限被屏蔽或者录音设备损坏!\n请在设置中检查是否开启权限!",
+ Toast.LENGTH_SHORT).show();
+ mDialogManager.dimissDialog();
+ mAudioManager.cancel();
+ reset();
+ break;
+ default:
+ break;
+ }
+ }
+
+ };
+
+ /**
+ * 先实现两个参数的构造方法,布局会默认引用这个构造方法, 用一个 构造参数的构造方法来引用这个方法 * @param context
+ */
+
+ public AudioRecordButton(Context context) {
+ this(context, null);
+ // TODO Auto-generated constructor stub
+ }
+
+ public AudioRecordButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mDialogManager = new DialogManager(getContext());
+
+ try {
+ FileSaveUtil.createSDDirectory(FileSaveUtil.voice_dir);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ mAudioManager = AudioManager.getInstance(FileSaveUtil.voice_dir);
+ mAudioManager.setOnAudioStageListener(this);
+ mAudioManager.setHandle(mp3handler);
+ setOnLongClickListener(new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ // TODO Auto-generated method
+ try {
+ FileSaveUtil.createSDDirectory(saveDir);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ mAudioManager.setVocDir(saveDir);
+ mListener.onStart();
+ mReady = true;
+ mAudioManager.prepareAudio();
+ return false;
+ }
+ });
+ // TODO Auto-generated constructor stub
+ }
+
+ public void setSaveDir(String saveDir) {
+ this.saveDir = saveDir + saveDir;
+ }
+
+ /**
+ * 录音完成后的回调,回调给activiy,可以获得mtime和文件的路径
+ */
+ public interface AudioFinishRecorderListener {
+ void onStart();
+
+ void onFinished(float seconds, String filePath);
+ }
+
+ private AudioFinishRecorderListener mListener;
+
+ public void setAudioFinishRecorderListener(
+ AudioFinishRecorderListener listener) {
+ mListener = listener;
+ }
+
+ // 获取音量大小的runnable
+ private Runnable mGetVoiceLevelRunnable = new Runnable() {
+
+ @Override
+ public void run() {
+ // TODO Auto-generated method stub
+ while (isRecording) {
+ try {
+ Thread.sleep(100);
+ mTime += 0.1f;
+ mhandler.sendEmptyMessage(MSG_VOICE_CHANGE);
+ if (mTime >= OVERTIME) {
+ mTime = 60;
+ mhandler.sendEmptyMessage(MSG_OVERTIME_SEND);
+ isRecording = false;
+ break;
+ }
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+
+ // 准备三个常量
+ private static final int MSG_AUDIO_PREPARED = 0X110;
+ private static final int MSG_VOICE_CHANGE = 0X111;
+ private static final int MSG_DIALOG_DIMISS = 0X112;
+ private static final int MSG_OVERTIME_SEND = 0X113;
+
+ private Handler mhandler = new Handler() {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_AUDIO_PREPARED:
+ // 显示应该是在audio end prepare之后回调
+ if (isTouch) {
+ mTime = 0;
+ mDialogManager.showRecordingDialog();
+ isRecording = true;
+ new Thread(mGetVoiceLevelRunnable).start();
+ }
+ // 需要开启一个线程来变换音量
+ break;
+ case MSG_VOICE_CHANGE:
+ mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(3));
+ break;
+ case MSG_DIALOG_DIMISS:
+ isRecording = false;
+ mDialogManager.dimissDialog();
+ break;
+ case MSG_OVERTIME_SEND:
+ mDialogManager.tooLong();
+ mhandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1300);// 持续1.3s
+ if (mListener != null) {// 并且callbackActivity,保存录音
+ File file = new File(mAudioManager.getCurrentFilePath());
+ if (FileSaveUtil.isFileExists(file)) {
+ mListener.onFinished(mTime,
+ mAudioManager.getCurrentFilePath());
+ }else{
+ mp3handler.sendEmptyMessage(AudioManager.MSG_ERROR_AUDIO_RECORD);
+ }
+ }
+ isRecording = false;
+ reset();// 恢复标志位
+ break;
+ }
+ };
+ };
+
+ // 在这里面发送一个handler的消息
+ @Override
+ public void wellPrepared() {
+ // TODO Auto-generated method stub
+ mhandler.sendEmptyMessage(MSG_AUDIO_PREPARED);
+ }
+
+ /**
+ * 直接复写这个监听函数
+ */
+ private boolean isTouch = false;
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ // TODO Auto-generated method stub
+ int action = event.getAction();
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ isTouch = true;
+ changeState(STATE_RECORDING);
+ break;
+ case MotionEvent.ACTION_MOVE:
+
+ if (isRecording) {
+
+ // 根据x,y来判断用户是否想要取消
+ if (wantToCancel(x, y)) {
+ changeState(STATE_WANT_TO_CANCEL);
+ } else {
+ changeState(STATE_RECORDING);
+ }
+
+ }
+
+ break;
+ case MotionEvent.ACTION_UP:
+ // 首先判断是否有触发onlongclick事件,没有的话直接返回reset
+ isTouch = false;
+ if (!mReady) {
+ reset();
+ return super.onTouchEvent(event);
+ }
+ // 如果按的时间太短,还没准备好或者时间录制太短,就离开了,则显示这个dialog
+ if (!isRecording || mTime < 0.6f) {
+ mDialogManager.tooShort();
+ mAudioManager.cancel();
+ mhandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1300);// 持续1.3s
+ } else if (mCurrentState == STATE_RECORDING) {// 正常录制结束
+ mDialogManager.dimissDialog();
+ mAudioManager.release();// release释放一个mediarecorder
+ if (mListener != null) {// 并且callbackActivity,保存录音
+ BigDecimal b = new BigDecimal(mTime);
+ float f1 = b.setScale(1, BigDecimal.ROUND_HALF_UP)
+ .floatValue();
+ File file = new File(mAudioManager.getCurrentFilePath());
+ if (FileSaveUtil.isFileExists(file)) {
+ mListener.onFinished(f1,mAudioManager.getCurrentFilePath());
+ }else{
+ mp3handler.sendEmptyMessage(AudioManager.MSG_ERROR_AUDIO_RECORD);
+ }
+ }
+ } else if (mCurrentState == STATE_WANT_TO_CANCEL) {
+ mAudioManager.cancel();
+ mDialogManager.dimissDialog();
+ }
+ isRecording = false;
+ reset();// 恢复标志位
+
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ isTouch = false;
+ reset();
+ break;
+
+ }
+
+ return super.onTouchEvent(event);
+ }
+
+ /**
+ * 回复标志位以及状态
+ */
+ private void reset() {
+ // TODO Auto-generated method stub
+ isRecording = false;
+ changeState(STATE_NORMAL);
+ mReady = false;
+ mTime = 0;
+ }
+
+ private boolean wantToCancel(int x, int y) {
+ // TODO Auto-generated method stub
+
+ if (x < 0 || x > getWidth()) {// 判断是否在左边,右边,上边,下边
+ return true;
+ }
+ if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private void changeState(int state) {
+ // TODO Auto-generated method stub
+ if (mCurrentState != state) {
+ mCurrentState = state;
+ switch (mCurrentState) {
+ case STATE_NORMAL:
+ setBackgroundResource(R.drawable.button_recordnormal);
+ setText(R.string.normal);
+
+ break;
+ case STATE_RECORDING:
+ setBackgroundResource(R.drawable.button_recording);
+ setText(R.string.recording);
+ if (isRecording) {
+ mDialogManager.recording();
+ // 复写dialog.recording();
+ }
+ break;
+
+ case STATE_WANT_TO_CANCEL:
+ setBackgroundResource(R.drawable.button_recording);
+ setText(R.string.want_to_cancle);
+ // dialog want to cancel
+ mDialogManager.wantToCancel();
+ break;
+
+ }
+ }
+
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+}
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/BubbleImageView.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/BubbleImageView.java
new file mode 100644
index 0000000..6ba3d6c
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/BubbleImageView.java
@@ -0,0 +1,139 @@
+package com.shunzhi.mychartlibrary.widget;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.NinePatch;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.Display;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.SimpleTarget;
+
+
+public class BubbleImageView extends ImageView {
+ private Context context;
+ private Bitmap iconBitmap;
+ private int res;
+ private static final int OK_INT = 0x0001;
+ private static final int ERROR_INT = 0x0000;
+ @SuppressLint("HandlerLeak")
+ private Handler bitmapHandler = new Handler() {
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case OK_INT:
+ Bitmap bitmap_bg = BitmapFactory.decodeResource(getResources(),
+ res);
+ final Bitmap bp = getRoundCornerImage(bitmap_bg, iconBitmap);
+ setImageBitmap(bp);
+ break;
+ case ERROR_INT:
+ break;
+ }
+ };
+ };
+
+ public BubbleImageView(Context context) {
+ super(context);
+ this.context = context;
+ }
+
+ public BubbleImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ this.context = context;
+ }
+
+ public BubbleImageView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ this.context = context;
+ }
+
+ public void load(String url, int res, int placeHolderPic) {
+ this.setImageResource(placeHolderPic);
+ this.res = res;
+ Glide.with(context).load(url).asBitmap().into(new SimpleTarget() {
+ @Override
+ public void onResourceReady(Bitmap resource, GlideAnimation super Bitmap> glideAnimation) {
+ if (resource != null) {
+ iconBitmap = resource;
+ bitmapHandler.sendEmptyMessage(OK_INT);
+ }
+ }
+ });
+ }
+
+ public void setLocalImageBitmap(Bitmap bm, int res) {
+ // TODO Auto-generated method stub
+ Bitmap bitmap_bg = BitmapFactory.decodeResource(getResources(), res);
+ final Bitmap bp = getRoundCornerImage(bitmap_bg, bm);
+ setImageBitmap(bp);
+ }
+
+ public Bitmap getRoundCornerImage(Bitmap bitmap_bg, Bitmap bitmap_in) {
+ int width = bitmap_in.getWidth();
+ int height = bitmap_in.getHeight();
+ if(height != 0){
+ double scale = (width * 1.00) / height;
+ if (width >= height) {
+ width = getBitmapWidth();
+ height = (int) (width / scale);
+ } else {
+ height = getBitmapHeight();
+ width = (int) (height * scale);
+ }
+ }else{
+ width = 100;
+ height = 100;
+ }
+ Bitmap roundConcerImage = Bitmap.createBitmap(width, height,
+ Config.ARGB_8888);
+ Canvas canvas = new Canvas(roundConcerImage);
+ Paint paint = new Paint();
+ Rect rect = new Rect(0, 0, width, height);
+ Rect rectF = new Rect(0, 0, bitmap_in.getWidth(), bitmap_in.getHeight());
+ paint.setAntiAlias(true);
+ NinePatch patch = new NinePatch(bitmap_bg,
+ bitmap_bg.getNinePatchChunk(), null);
+ patch.draw(canvas, rect);
+ paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+ canvas.drawBitmap(bitmap_in, rectF, rect, paint);
+ return roundConcerImage;
+ }
+
+ // 获取屏幕的宽度
+ @SuppressWarnings("deprecation")
+ public int getScreenWidth(Context context) {
+ WindowManager manager = (WindowManager) context
+ .getSystemService(Context.WINDOW_SERVICE);
+ Display display = manager.getDefaultDisplay();
+ return display.getWidth();
+ }
+
+ // 获取屏幕的高度
+ @SuppressWarnings("deprecation")
+ public int getScreenHeight(Context context) {
+ WindowManager manager = (WindowManager) context
+ .getSystemService(Context.WINDOW_SERVICE);
+ Display display = manager.getDefaultDisplay();
+ return display.getHeight();
+ }
+
+ public int getBitmapWidth() {
+ return getScreenWidth(context) / 3;
+ }
+
+ public int getBitmapHeight() {
+ return getScreenHeight(context) / 4;
+ }
+}
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/ChatBottomView.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/ChatBottomView.java
new file mode 100644
index 0000000..ff0eead
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/ChatBottomView.java
@@ -0,0 +1,70 @@
+package com.shunzhi.mychartlibrary.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.shunzhi.mychartlibrary.R;
+
+public class ChatBottomView extends LinearLayout{
+ private View baseView;
+ private LinearLayout imageGroup;
+ private LinearLayout cameraGroup;
+ private LinearLayout phraseGroup;
+ private HeadIconSelectorView.OnHeadIconClickListener onHeadIconClickListener;
+ public static final int FROM_CAMERA = 1;
+ public static final int FROM_GALLERY = 2;
+ public static final int FROM_PHRASE = 3;
+ public ChatBottomView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ // TODO Auto-generated constructor stub
+ findView();
+ init();
+ }
+
+ private void findView(){
+ baseView = LayoutInflater.from(getContext()).inflate(R.layout.layout_tongbaobottom, this);
+ imageGroup = (LinearLayout) baseView.findViewById(R.id.image_bottom_group);
+ cameraGroup = (LinearLayout) baseView.findViewById(R.id.camera_group);
+ phraseGroup = (LinearLayout) baseView.findViewById(R.id.phrase_group);
+ }
+ private void init(){
+ cameraGroup.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (null != onHeadIconClickListener) {
+ onHeadIconClickListener.onClick(FROM_CAMERA);
+ }
+ }
+ });
+ imageGroup.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+
+ if (null != onHeadIconClickListener) {
+ onHeadIconClickListener.onClick(FROM_GALLERY);
+ }
+ }
+ });
+ phraseGroup.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+
+ if (null != onHeadIconClickListener) {
+ onHeadIconClickListener.onClick(FROM_PHRASE);
+ }
+ }
+ });
+ }
+
+ public void setOnHeadIconClickListener(
+ HeadIconSelectorView.OnHeadIconClickListener onHeadIconClickListener) {
+ // TODO Auto-generated method stub
+ this.onHeadIconClickListener = onHeadIconClickListener;
+ }
+}
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/CustomShapeTransformation.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/CustomShapeTransformation.java
new file mode 100644
index 0000000..10cd876
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/CustomShapeTransformation.java
@@ -0,0 +1,109 @@
+package com.shunzhi.mychartlibrary.widget;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.drawable.Drawable;
+import android.support.v4.content.ContextCompat;
+import android.view.Display;
+import android.view.WindowManager;
+
+import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
+import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
+import com.bumptech.glide.load.resource.bitmap.TransformationUtils;
+
+/**
+ * Created by Mao Jiqing on 2016/11/4.
+ */
+
+public class CustomShapeTransformation extends BitmapTransformation {
+
+ private Paint mPaint; // 画笔
+ private Context mContext;
+ private int mShapeRes; // 形状的drawable资源
+
+ public CustomShapeTransformation(Context context, int shapeRes) {
+ super(context);
+ mContext = context;
+ mShapeRes = shapeRes;
+ // 实例化Paint对象,并设置Xfermode为SRC_IN
+ mPaint = new Paint();
+ mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ }
+
+ @Override
+ // 复写该方法,完成图片的转换
+ public Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
+ // 获取到形状资源的Drawable对象
+ Drawable shape = ContextCompat.getDrawable(mContext, mShapeRes);
+ int width = toTransform.getWidth();
+ int height = toTransform.getHeight();
+ if(height != 0){
+ double scale = (width * 1.00) / height;
+ if (width >= height) {
+ width = getBitmapWidth();
+ height = (int) (width / scale);
+ } else {
+ height = getBitmapHeight();
+ width = (int) (height * scale);
+ }
+ }else{
+ width = 100;
+ height = 100;
+ }
+ // 居中裁剪图片,调用Glide库中TransformationUtils类的centerCrop()方法完成裁剪,保证图片居中且填满
+ final Bitmap toReuse = pool.get(width, height, toTransform.getConfig() != null
+ ? toTransform.getConfig() : Bitmap.Config.ARGB_8888);
+ Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, width, height);
+ if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {
+ toReuse.recycle();
+ }
+
+ // 根据算出的宽高新建Bitmap对象并设置到画布上
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ // 设置形状的大小与图片的大小一致
+ shape.setBounds(0, 0, width, height);
+ // 将图片画到画布上
+ shape.draw(canvas);
+ // 将裁剪后的图片画得画布上
+ canvas.drawBitmap(transformed, 0, 0, mPaint);
+
+ return bitmap;
+ }
+
+ @Override
+ public String getId() {
+ // 用于缓存的唯一标识符
+ return "CustomShapeTransformation" + mShapeRes;
+ }
+
+ // 获取屏幕的宽度
+ @SuppressWarnings("deprecation")
+ public int getScreenWidth(Context context) {
+ WindowManager manager = (WindowManager) context
+ .getSystemService(Context.WINDOW_SERVICE);
+ Display display = manager.getDefaultDisplay();
+ return display.getWidth();
+ }
+
+ // 获取屏幕的高度
+ @SuppressWarnings("deprecation")
+ public int getScreenHeight(Context context) {
+ WindowManager manager = (WindowManager) context
+ .getSystemService(Context.WINDOW_SERVICE);
+ Display display = manager.getDefaultDisplay();
+ return display.getHeight();
+ }
+
+ public int getBitmapWidth() {
+ return getScreenWidth(mContext) / 3;
+ }
+
+ public int getBitmapHeight() {
+ return getScreenHeight(mContext) / 4;
+ }
+}
\ No newline at end of file
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/DialogManager.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/DialogManager.java
new file mode 100644
index 0000000..18bfef7
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/DialogManager.java
@@ -0,0 +1,148 @@
+package com.shunzhi.mychartlibrary.widget;
+
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.shunzhi.mychartlibrary.R;
+import com.shunzhi.mychartlibrary.utils.ScreenUtil;
+
+@SuppressLint("InflateParams")
+public class DialogManager {
+
+ /**
+ * 以下为dialog的初始化控件,包括其中的布局文件
+ */
+
+ private Dialog mDialog;
+
+ private ImageView mIcon;
+ private ImageView mVoice;
+
+ private TextView mLable;
+
+ private Context mContext;
+
+ public DialogManager(Context context) {
+ // TODO Auto-generated constructor stub
+ mContext = context;
+ }
+
+ public void showRecordingDialog() {
+ // TODO Auto-generated method stub
+
+ mDialog = new Dialog(mContext, R.style.Theme_audioDialog);
+ mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+ // 用layoutinflater来引用布局
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View view = inflater.inflate(R.layout.layout_voice_dialog_manager, null);
+ mDialog.setContentView(view);
+
+ mIcon = (ImageView) mDialog.findViewById(R.id.dialog_icon);
+ mVoice = (ImageView) mDialog.findViewById(R.id.dialog_voice);
+ mLable = (TextView) mDialog.findViewById(R.id.recorder_dialogtext);
+
+ Window dialogWindow = mDialog.getWindow();
+ WindowManager.LayoutParams lp = dialogWindow.getAttributes();
+ int width = ScreenUtil.getScreenWidth(mContext) / 2;
+ lp.width = width; // 宽度
+ lp.height = width; // 高度
+ dialogWindow.setAttributes(lp);
+ mDialog.setCancelable(false);
+ mDialog.show();
+
+ }
+
+ /**
+ * 设置正在录音时的dialog界面
+ */
+ public void recording() {
+ if (mDialog != null && mDialog.isShowing()) {
+ mIcon.setVisibility(View.GONE);
+ mVoice.setVisibility(View.VISIBLE);
+ mLable.setVisibility(View.VISIBLE);
+ mLable.setText(R.string.shouzhishanghua);
+ }
+ }
+
+ /**
+ * 取消界面
+ */
+ public void wantToCancel() {
+ // TODO Auto-generated method stub
+ if (mDialog != null && mDialog.isShowing()) {
+ mIcon.setVisibility(View.VISIBLE);
+ mVoice.setVisibility(View.GONE);
+ mLable.setVisibility(View.VISIBLE);
+
+ mIcon.setImageResource(R.mipmap.cancel);
+ mLable.setText(R.string.want_to_cancle);
+ }
+
+ }
+
+ // 时间过短
+ public void tooShort() {
+ // TODO Auto-generated method stub
+ if (mDialog != null && mDialog.isShowing()) {
+ mIcon.setVisibility(View.VISIBLE);
+ mVoice.setVisibility(View.GONE);
+ mLable.setVisibility(View.VISIBLE);
+
+ mIcon.setImageResource(R.mipmap.voice_to_short);
+ mLable.setText(R.string.tooshort);
+ }
+
+ }
+ // 时间过长
+ public void tooLong() {
+ // TODO Auto-generated method stub
+ if (mDialog != null && mDialog.isShowing()) {
+ mIcon.setVisibility(View.VISIBLE);
+ mVoice.setVisibility(View.GONE);
+ mLable.setVisibility(View.VISIBLE);
+
+ mIcon.setImageResource(R.mipmap.voice_to_short);
+ mLable.setText(R.string.toolong);
+ }
+
+ }
+ // 隐藏dialog
+ public void dimissDialog() {
+ // TODO Auto-generated method stub
+
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+
+ }
+
+ public void updateVoiceLevel(int level) {
+ // TODO Auto-generated method stub
+
+ if (mDialog != null && mDialog.isShowing()) {
+ int resId;
+ if(level >= 1 && level < 2){
+ resId = mContext.getResources().getIdentifier("tb_voice1",
+ "mipmap", mContext.getPackageName());
+ }else if(level >= 2 && level < 3){
+ resId = mContext.getResources().getIdentifier("tb_voice2",
+ "mipmap", mContext.getPackageName());
+ }else{
+ resId = mContext.getResources().getIdentifier("tb_voice3",
+ "mipmap", mContext.getPackageName());
+ }
+ mVoice.setImageResource(resId);
+ }
+
+ }
+
+}
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/ExpandGridView.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/ExpandGridView.java
new file mode 100644
index 0000000..14a197b
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/ExpandGridView.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2013-2014 EaseMob Technologies. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.shunzhi.mychartlibrary.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.GridView;
+
+public class ExpandGridView extends GridView {
+
+ public ExpandGridView(Context context) {
+ super(context);
+ }
+
+ public ExpandGridView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
+ super.onMeasure(widthMeasureSpec, expandSpec);
+ }
+
+
+}
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/GifTextView.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/GifTextView.java
new file mode 100644
index 0000000..a4f7063
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/GifTextView.java
@@ -0,0 +1,303 @@
+package com.shunzhi.mychartlibrary.widget;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.ImageSpan;
+import android.util.AttributeSet;
+import android.widget.EditText;
+
+
+import com.shunzhi.mychartlibrary.utils.FaceData;
+import com.shunzhi.mychartlibrary.utils.GifOpenHelper;
+import com.shunzhi.mychartlibrary.utils.ScreenUtil;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class GifTextView extends EditText {
+ /**
+ * 注:如果获取的gif帧与帧之间的时间间隔都不相同,建议调个固定的,最好的方法是将gif图的间隔设置相同
+ */
+ private static final int DELAYED = 300;
+
+ /**
+ * @author Dragon SpanInfo
+ * 类用于存储一个要显示的图片(动态或静态)的信息,包括分解后的每一帧mapList、替代文字的起始位置、终止位置
+ * 、帧的总数、当前需要显示的帧、帧与帧之间的时间间隔
+ */
+ private class SpanInfo {
+ ArrayList mapList;
+ @SuppressWarnings("unused")
+ int start, end, frameCount, currentFrameIndex, delay;
+
+ public SpanInfo() {
+ mapList = new ArrayList();
+ start = end = frameCount = currentFrameIndex = delay = 0;
+ }
+ }
+
+ /**
+ * spanInfoList 是一个SpanInfo的list ,用于处理一个TextView中出现多个要匹配的图片的情况
+ */
+ private ArrayList spanInfoList = null;
+ private Handler handler; // 用于处理从子线程TextView传来的消息
+ private String myText; // 存储textView应该显示的文本
+
+ /**
+ * 这三个构造方法一个也不要少,否则会产生CastException,注意在这三个构造函数中都为spanInfoList实例化,可能有些浪费
+ * ,但保证不会有空指针异常
+ *
+ * @param context
+ * @param attrs
+ * @param defStyle
+ */
+ @SuppressLint("NewApi")
+ public GifTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ // TODO Auto-generated constructor stub
+ // spanInfoList = new ArrayList();
+ GifTextView.this.setFocusableInTouchMode(false);
+ }
+
+ @SuppressLint("NewApi")
+ public GifTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ // TODO Auto-generated constructor stub
+ // spanInfoList = new ArrayList();
+ GifTextView.this.setFocusableInTouchMode(false);
+ }
+
+ @SuppressLint("NewApi")
+ public GifTextView(Context context) {
+ super(context);
+ // TODO Auto-generated constructor stub
+ // spanInfoList = new ArrayList();
+ GifTextView.this.setFocusableInTouchMode(false);
+ }
+
+ /**
+ * 对要显示在textView上的文本进行解析,看看是否文本中有需要与Gif或者静态图片匹配的文本 若有,那么调用parseGif
+ * 对该文本对应的Gif图片进行解析 或者嗲用parseBmp解析静态图片
+ *
+ * @param inputStr
+ */
+ private boolean parseText(String inputStr) {
+ myText = inputStr;
+// Pattern mPattern = Pattern.compile("\\[:..\\]|\\[:...\\]");
+ Pattern mPattern = Pattern.compile("\\[[^\\]]+\\]");
+ Matcher mMatcher = mPattern.matcher(inputStr);
+ boolean hasGif = false;
+ while (mMatcher.find()) {
+ String faceName = mMatcher.group();
+ Integer faceId = null;
+ /**
+ * 这里匹配时用到了图片库,即一个专门存放图片id和其匹配的名称的静态对象,这两个静态对象放在了FaceData.java
+ * 中,并采用了静态块的方法进行了初始化,不会有空指针异常
+ */
+ if ((faceId = FaceData.gifFaceInfo.get(faceName)) != null) {
+ if (isGif) {
+ parseGif(faceId, mMatcher.start(), mMatcher.end());
+ } else {
+ parseBmp(faceId, mMatcher.start(), mMatcher.end());
+ }
+ }
+ hasGif = true;
+ }
+ return hasGif;
+ }
+
+ /**
+ * 对静态图片进行解析:
+ * 创建一个SpanInfo对象,帧数设为1,按照下面的参数设置,最后不要忘记将SpanInfo对象添加进spanInfoList中, 否则不会显示
+ *
+ * @param resourceId
+ * @param start
+ * @param end
+ */
+ @SuppressWarnings("unused")
+ private void parseBmp(int resourceId, int start, int end) {
+ Bitmap bitmap = BitmapFactory.decodeResource(getContext()
+ .getResources(), resourceId);
+ ImageSpan imageSpan = new ImageSpan(getContext(), bitmap);
+ SpanInfo spanInfo = new SpanInfo();
+ spanInfo.currentFrameIndex = 0;
+ spanInfo.frameCount = 1;
+ spanInfo.start = start;
+ spanInfo.end = end;
+ spanInfo.delay = 100;
+ spanInfo.mapList.add(bitmap);
+ spanInfoList.add(spanInfo);
+
+ }
+
+ /**
+ * 解析Gif图片,与静态图片唯一的不同是这里需要调用GifOpenHelper类读取Gif返回一系一组bitmap(用for 循环把这一
+ * 组的bitmap存储在SpanInfo.mapList中,此时的frameCount参数也大于1了)
+ *
+ * @param resourceId
+ * @param start
+ * @param end
+ */
+ private void parseGif(int resourceId, int start, int end) {
+
+ GifOpenHelper helper = new GifOpenHelper();
+ helper.read(getContext().getResources().openRawResource(resourceId));
+ SpanInfo spanInfo = new SpanInfo();
+ spanInfo.currentFrameIndex = 0;
+ spanInfo.frameCount = helper.getFrameCount();
+ spanInfo.start = start;
+ spanInfo.end = end;
+ spanInfo.mapList.add(helper.getImage());
+ for (int i = 1; i < helper.getFrameCount(); i++) {
+ spanInfo.mapList.add(helper.nextBitmap());
+ }
+ spanInfo.delay = helper.nextDelay(); // 获得每一帧之间的延迟
+ spanInfoList.add(spanInfo);
+
+ }
+
+ private boolean isGif;
+
+ /**
+ * GifTextView 与外部对象的接口,以后设置文本内容时使用setSpanText() 而不再是setText();
+ *
+ * @param handler
+ * @param text
+ */
+ public void setSpanText(Handler handler, final String text, boolean isGif) {
+ this.handler = handler; // 获得UI的Handler
+ this.isGif = isGif;
+ spanInfoList = new ArrayList();
+// ThreadPoolUtils.execute(new Runnable() {
+//
+// @Override
+// public void run() {
+//// // TODO Auto-generated method stub
+ if (parseText(text)) {// 对String对象进行解析
+// mStartHandler.sendEmptyMessage(0);
+ if (parseMessage(this)) {
+ startPost();
+ }
+ } else {
+// mStartHandler.sendEmptyMessage(1);
+ setText(myText);
+ }
+// }
+// });
+ }
+
+// private StartHandler mStartHandler = new StartHandler(this);
+//
+// public static class StartHandler extends Handler {
+// private final WeakReference mGifWeakReference;
+//
+// public StartHandler(GifTextView gifTextView) {
+// mGifWeakReference = new WeakReference(gifTextView);
+// }
+//
+// @Override
+// public void handleMessage(Message msg) {
+// GifTextView gifTextView = mGifWeakReference.get();
+// if (gifTextView != null) {
+// if (msg.what == 0) {
+// gifTextView.startPost();
+// } else if (msg.what == 1) {
+// gifTextView.setText(gifTextView.myText);
+// }
+// }
+// }
+// }
+
+ public boolean parseMessage(GifTextView gifTextView) {
+ if (gifTextView.myText != null && !gifTextView.myText.equals("")) {
+ SpannableString sb = new SpannableString("" + gifTextView.myText); // 获得要显示的文本
+ int gifCount = 0;
+ SpanInfo info = null;
+ for (int i = 0; i < gifTextView.spanInfoList.size(); i++) { // for循环,处理显示多个图片的问题
+ info = gifTextView.spanInfoList.get(i);
+ if (info.mapList.size() > 1) {
+ /*
+ * gifCount用来区分是Gif还是BMP,若是gif gifCount>0
+ * ,否则gifCount=0
+ */
+ gifCount++;
+
+ }
+ Bitmap bitmap = info.mapList
+ .get(info.currentFrameIndex);
+ info.currentFrameIndex = (info.currentFrameIndex + 1)
+ % (info.frameCount);
+ /**
+ * currentFrameIndex
+ * 用于控制当前应该显示的帧的序号,每次显示之后currentFrameIndex 应该加1
+ * ,加到frameCount后再变成0循环显示
+ */
+ int size = ScreenUtil.dip2px(gifTextView.getContext(), 30);
+ if (gifCount != 0) {
+ bitmap = Bitmap.createScaledBitmap(bitmap, size,
+ size, true);
+
+ } else {
+ bitmap = Bitmap.createScaledBitmap(bitmap, size,
+ size, true);
+ }
+ ImageSpan imageSpan = new ImageSpan(gifTextView.getContext(),
+ bitmap);
+ if (info.end <= sb.length()) {
+ sb.setSpan(imageSpan, info.start, info.end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else {
+ break;
+ }
+
+ }
+ // 对所有的图片对应的ImageSpan完成设置后,调用TextView的setText方法设置文本
+ gifTextView.setText(sb);
+ if (gifCount != 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public TextRunnable rTextRunnable;
+
+ public void startPost() {
+ rTextRunnable = new TextRunnable(this); // 生成Runnable对象
+ handler.post(rTextRunnable); // 利用UI线程的Handler 将r添加进消息队列中。
+ }
+
+ public static final class TextRunnable implements Runnable {
+ private final WeakReference mWeakReference;
+
+ public TextRunnable(GifTextView f) {
+ mWeakReference = new WeakReference(f);
+ }
+
+ @Override
+ public void run() {
+ // TODO Auto-generated method stub
+ GifTextView gifTextView = mWeakReference.get();
+ if (gifTextView != null) {
+ /**
+ * 这一步是为了节省内存而是用,即如果文本中只有静态图片没有动态图片,那么该线程就此终止,不会重复执行
+ * 。而如果有动图,那么会一直执行
+ */
+ if (gifTextView.parseMessage(gifTextView)) {
+ gifTextView.handler.postDelayed(this, DELAYED);
+ }
+ }
+ }
+ }
+
+}
+
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/HeadIconSelectorView.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/HeadIconSelectorView.java
new file mode 100644
index 0000000..b3c1252
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/HeadIconSelectorView.java
@@ -0,0 +1,331 @@
+package com.shunzhi.mychartlibrary.widget;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.ScaleAnimation;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import com.shunzhi.mychartlibrary.R;
+
+
+public class HeadIconSelectorView extends RelativeLayout implements
+ GestureDetector.OnGestureListener {
+
+ private View baseView;
+
+ private RelativeLayout mainRl;
+ private LinearLayout bottomLl;
+ private LinearLayout cameraLl;
+ private LinearLayout galleryLl;
+ private LinearLayout cancelLl;
+
+ private GestureDetector gestureDetector; // 手势检测器
+ private boolean isAnimationing = false;
+ private OnHeadIconClickListener onHeadIconClickListener;
+ public static final int FROM_CAMERA = 2;
+ public static final int FROM_GALLERY = 3;
+ public static final int CANCEL = 4;
+ public static final int BLANK_CANCEL = 5;
+
+ public HeadIconSelectorView(Context context) {
+ super(context);
+ init();
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ private void init() {
+ findView();
+ this.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return gestureDetector.onTouchEvent(event);
+ }
+ });
+ bottomLl.setVisibility(View.INVISIBLE);
+ mainRl.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (null != onHeadIconClickListener) {
+ onHeadIconClickListener.onClick(BLANK_CANCEL);
+ }
+ cancel();
+ }
+ });
+ cancelLl.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (null != onHeadIconClickListener) {
+ onHeadIconClickListener.onClick(CANCEL);
+ }
+ cancel();
+ }
+ });
+ cameraLl.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (null != onHeadIconClickListener) {
+ onHeadIconClickListener.onClick(FROM_CAMERA);
+ }
+ cancel();
+ }
+ });
+ galleryLl.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+
+ if (null != onHeadIconClickListener) {
+ onHeadIconClickListener.onClick(FROM_GALLERY);
+ }
+ cancel();
+ }
+ });
+ }
+
+ @SuppressWarnings("deprecation")
+ private void findView() {
+ gestureDetector = new GestureDetector(this);
+ baseView = LayoutInflater.from(getContext()).inflate(
+ R.layout.layout_view_headicon, this);
+ mainRl = (RelativeLayout) baseView.findViewById(R.id.head_icon_main_rl);
+ bottomLl = (LinearLayout) baseView.findViewById(R.id.head_icon_main_ll);
+ cameraLl = (LinearLayout) baseView
+ .findViewById(R.id.head_icon_camera_ll);
+ galleryLl = (LinearLayout) baseView
+ .findViewById(R.id.head_icon_gallery_ll);
+ cancelLl = (LinearLayout) baseView
+ .findViewById(R.id.head_icon_cancel_ll);
+ }
+
+ protected void bottomViewFlyIn() {
+ final ScaleAnimation sa1 = new ScaleAnimation(1, 1, 0, 1.2f,
+ Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 1f);
+ sa1.setDuration(250);
+ final ScaleAnimation sa2 = new ScaleAnimation(1, 1, 1.2f, 1,
+ Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 1f);
+ sa2.setDuration(150);
+ sa1.setAnimationListener(new AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ isAnimationing = true;
+ bottomLl.setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ isAnimationing = false;
+ bottomLl.startAnimation(sa2);
+ }
+ });
+ bottomLl.startAnimation(sa1);
+ }
+
+ protected void bottomViewFlyOut() {
+ final ScaleAnimation sa1 = new ScaleAnimation(1, 1, 1, 1.2f,
+ Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 1f);
+ sa1.setDuration(150);
+ final ScaleAnimation sa2 = new ScaleAnimation(1, 1, 1.2f, 0,
+ Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 1f);
+ sa2.setDuration(250);
+ sa1.setAnimationListener(new AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ isAnimationing = true;
+ bottomLl.setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ bottomLl.startAnimation(sa2);
+ }
+ });
+ sa2.setAnimationListener(new AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ isAnimationing = false;
+ bottomLl.setVisibility(INVISIBLE);
+ flyOut();
+ }
+ });
+ bottomLl.startAnimation(sa1);
+ }
+
+ protected void cancel() {
+ if (!isAnimationing) {
+ if (bottomLl.getVisibility() == VISIBLE) {
+ bottomViewFlyOut();
+ return;
+ }
+ }
+ }
+
+ public void flyIn() {
+ AlphaAnimation aa = new AlphaAnimation(0, 1);
+ aa.setDuration(300);
+ aa.setAnimationListener(new AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ isAnimationing = true;
+ setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ isAnimationing = false;
+ bottomViewFlyIn();
+ }
+ });
+ startAnimation(aa);
+ }
+
+ public void flyOut() {
+ AlphaAnimation aa = new AlphaAnimation(1, 0);
+ aa.setDuration(300);
+ aa.setAnimationListener(new AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ isAnimationing = true;
+ setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ isAnimationing = false;
+ destroy();
+ }
+ });
+ startAnimation(aa);
+ }
+
+ protected void destroy() {
+ if (this.getParent() != null) {
+ ((ViewGroup) this.getParent()).removeView(this);
+ }
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (!isAnimationing) {
+ if (bottomLl.getVisibility() == VISIBLE) {
+ bottomViewFlyOut();
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ public OnHeadIconClickListener getOnHeadIconClickListener() {
+ return onHeadIconClickListener;
+ }
+
+ public void setOnHeadIconClickListener(
+ OnHeadIconClickListener onHeadIconClickListener) {
+ this.onHeadIconClickListener = onHeadIconClickListener;
+ }
+
+ public interface OnHeadIconClickListener {
+ public void onClick(int from);
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return gestureDetector.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public void onShowPress(MotionEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ private float minVelocityY = 100f;// 10个像素每秒
+ private float minDistanceY = 100f;// 100个像素
+
+ // 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN,
+ // 多个ACTION_MOVE, 1个ACTION_UP触发
+ // e1:第1个ACTION_DOWN MotionEvent
+ // e2:最后一个ACTION_MOVE MotionEvent
+ // velocityX:X轴上的移动速度,像素/秒
+ // velocityY:Y轴上的移动速度,像素/秒
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ // 手势从上到下且移动速度较快
+ if (e2.getY() - e1.getY() > minDistanceY && velocityY > minVelocityY) {
+ cancel();
+ }
+ return false;
+ }
+}
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/MediaManager.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/MediaManager.java
new file mode 100644
index 0000000..8af9689
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/MediaManager.java
@@ -0,0 +1,84 @@
+package com.shunzhi.mychartlibrary.widget;
+
+
+
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+
+import java.io.IOException;
+
+public class MediaManager {
+
+
+ private static MediaPlayer mPlayer;
+
+ private static boolean isPause;
+
+ public static void playSound(String filePathString,
+ OnCompletionListener onCompletionListener) {
+ // TODO Auto-generated method stub
+ if (mPlayer==null) {
+ mPlayer=new MediaPlayer();
+ //保险起见,设置报错监听
+ mPlayer.setOnErrorListener(new OnErrorListener() {
+
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ // TODO Auto-generated method stub
+ mPlayer.reset();
+ return false;
+ }
+ });
+ }else {
+ mPlayer.reset();//就恢复
+ }
+
+ try {
+ mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mPlayer.setOnCompletionListener(onCompletionListener);
+ mPlayer.setDataSource(filePathString);
+ mPlayer.prepare();
+ mPlayer.start();
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (SecurityException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalStateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ //停止函数
+ public static void pause(){
+ if (mPlayer!=null&&mPlayer.isPlaying()) {
+ mPlayer.pause();
+ isPause=true;
+ }
+ }
+
+ //继续
+ public static void resume()
+ {
+ if (mPlayer!=null&&isPause) {
+ mPlayer.start();
+ isPause=false;
+ }
+ }
+
+
+ public static void release()
+ {
+ if (mPlayer!=null) {
+ mPlayer.release();
+ mPlayer=null;
+ }
+ }
+}
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/PullToRefreshLayout.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/PullToRefreshLayout.java
new file mode 100644
index 0000000..eb57618
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/PullToRefreshLayout.java
@@ -0,0 +1,250 @@
+package com.shunzhi.mychartlibrary.widget.pulltorefresh;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Color;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.ViewDragHelper;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.shunzhi.mychartlibrary.utils.ScreenUtil;
+
+
+/**
+ * Created by Mao Jiqing on 2016/9/27.
+ */
+public class PullToRefreshLayout extends LinearLayout {
+ private ViewDragHelper VDH;
+ private View myList;
+ private TextView pullText;
+ private pulltorefreshNotifier pullNotifier;
+ private boolean isPull = true;
+
+ public PullToRefreshLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ // TODO Auto-generated constructor stub
+// init();
+ VDH = ViewDragHelper.create(this, 10.0f, new DragHelperCallback());
+ }
+
+ public void setSlideView(View view) {
+ init(view);
+ }
+
+ private void init(View view) {
+ LayoutParams lp1 = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ myList = view;
+ myList.setBackgroundColor(Color.parseColor("#FFFFFF"));
+ myList.setLayoutParams(lp1);
+ LayoutParams lp2 = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ScreenUtil.dip2px(getContext(), 100));
+ pullText = new TextView(getContext());
+ pullText.setText("下拉加载更多");
+ pullText.setBackgroundColor(Color.parseColor("#FFFFFF"));
+ pullText.setGravity(Gravity.CENTER);
+ pullText.setLayoutParams(lp2);
+ setOrientation(LinearLayout.VERTICAL);
+ addView(pullText);
+ addView(myList);
+ }
+
+ public View returnMylist() {
+ return myList;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+
+ int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
+ setMeasuredDimension(
+ resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
+ resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ if (pullText.getTop() == 0) {
+ viewHeight = pullText.getMeasuredHeight();
+ pullText.layout(l, 0, r, viewHeight);
+ myList.layout(l, 0, r, b);
+ pullText.offsetTopAndBottom(-viewHeight);
+ } else {
+ pullText.layout(l, pullText.getTop(), r, pullText.getBottom());
+ myList.layout(l, myList.getTop(), r, myList.getBottom());
+ }
+ }
+
+ /**
+ * 这是View的方法,该方法不支持android低版本(2.2、2.3)的操作系统,所以手动复制过来以免强制退出
+ */
+ public static int resolveSizeAndState(int size, int measureSpec,
+ int childMeasuredState) {
+ int result = size;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+ switch (specMode) {
+ case MeasureSpec.UNSPECIFIED:
+ result = size;
+ break;
+ case MeasureSpec.AT_MOST:
+ if (specSize < size) {
+ result = specSize | MEASURED_STATE_TOO_SMALL;
+ } else {
+ result = size;
+ }
+ break;
+ case MeasureSpec.EXACTLY:
+ result = specSize;
+ break;
+ }
+ return result | (childMeasuredState & MEASURED_STATE_MASK);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ boolean shouldIntercept = VDH.shouldInterceptTouchEvent(event) && isPull;
+ return shouldIntercept;
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ VDH.processTouchEvent(event);
+ return true;
+ }
+
+ /**
+ * 这是拖拽效果的主要逻辑
+ */
+ private class DragHelperCallback extends ViewDragHelper.Callback {
+
+ @Override
+ public void onViewPositionChanged(View changedView, int left, int top,
+ int dx, int dy) {
+ int childIndex = 1;
+ if (changedView == myList) {
+ childIndex = 2;
+ }
+ onViewPosChanged(childIndex, top);
+ }
+
+ @Override
+ public boolean tryCaptureView(View child, int pointerId) {
+ return true;
+ }
+
+ @Override
+ public int getViewVerticalDragRange(View child) {
+ return 1;
+ }
+
+ @Override
+ public void onViewReleased(View releasedChild, float xvel, float yvel) {
+ refreshOrNot(releasedChild, yvel);
+ }
+
+ @Override
+ public int clampViewPositionVertical(View child, int top, int dy) {
+ int finalTop = top;
+ if (child == pullText) {
+ if (top > 0) {
+ finalTop = 0;
+ }
+ } else if (child == myList) {
+ if (top < 0) {
+ finalTop = 0;
+ }
+ if (top >= viewHeight) {
+ pullText.setText("松开加载");
+ } else {
+ pullText.setText("下拉加载更多");
+ }
+ }
+ return child.getTop() + (finalTop - child.getTop()) / 2;
+ }
+ }
+
+ /**
+ * 滑动时view位置改变协调处理
+ *
+ * @param viewIndex
+ * 滑动view的index(1或2)
+ * @param posTop
+ * 滑动View的top位置
+ */
+ private static int viewHeight;
+
+ private void onViewPosChanged(int viewIndex, int posTop) {
+ if (viewIndex == 1) {
+ int offsetTopBottom = viewHeight + pullText.getTop()
+ - myList.getTop();
+ myList.offsetTopAndBottom(offsetTopBottom);
+ } else if (viewIndex == 2) {
+ int offsetTopBottom = myList.getTop() - viewHeight
+ - pullText.getTop();
+ pullText.offsetTopAndBottom(offsetTopBottom);
+ }
+ invalidate();
+ }
+
+ private void refreshOrNot(View releasedChild, float yvel) {
+ int finalTop = 0;
+ if (releasedChild == pullText) {
+ // 拖动第一个view松手
+ if (yvel < -50) {
+ finalTop = 0;
+ } else {
+ finalTop = viewHeight;
+ }
+ } else {
+ // 拖动第二个view松手
+ if (yvel > viewHeight - 5 || releasedChild.getTop() >= viewHeight) {
+ finalTop = viewHeight;
+ if (null != pullNotifier) {
+ pullNotifier.onPull();
+ }
+ pullText.setText("正在加载");
+ }
+ }
+
+ if (VDH.smoothSlideViewTo(myList, 0, finalTop)) {
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+ }
+
+ public void refreshComplete() {
+ if (VDH.smoothSlideViewTo(myList, 0, 0)) {
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+ }
+
+ @Override
+ public void computeScroll() {
+ if (VDH.continueSettling(true)) {
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+ }
+
+ public void setpulltorefreshNotifier(pulltorefreshNotifier pullNotifier) {
+ this.pullNotifier = pullNotifier;
+ }
+
+ public interface pulltorefreshNotifier {
+ public void onPull();
+ }
+
+ /**
+ * 禁止下拉
+ */
+ public void setPullGone() {
+ isPull = false;
+ }
+}
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/PullToRefreshListView.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/PullToRefreshListView.java
new file mode 100644
index 0000000..575db0c
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/PullToRefreshListView.java
@@ -0,0 +1,63 @@
+package com.shunzhi.mychartlibrary.widget.pulltorefresh;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ListView;
+
+/**
+ * Created by Mao Jiqing on 2016/10/10.
+ */
+public class PullToRefreshListView extends ListView {
+ boolean allowDragBottom = true;
+ float downY = 0;
+ boolean needConsumeTouch = true;
+
+ public PullToRefreshListView(Context context) {
+ super(context);
+ setDivider(null);
+ }
+
+ public PullToRefreshListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ downY = ev.getRawY();
+ needConsumeTouch = true;
+ if (getMyScrollY() == 0) {
+ allowDragBottom = true;
+ } else {
+ allowDragBottom = false;
+ }
+ } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
+ if (!needConsumeTouch) {
+ getParent().requestDisallowInterceptTouchEvent(false);
+ return false;
+ } else if (allowDragBottom) {
+ if (downY - ev.getRawY() < -2) {
+ needConsumeTouch = false;
+ getParent().requestDisallowInterceptTouchEvent(false);
+ return false;
+ }
+ }
+ }
+ getParent().requestDisallowInterceptTouchEvent(needConsumeTouch);
+ return super.dispatchTouchEvent(ev);
+ }
+
+ public int getMyScrollY() {
+ View c = getChildAt(0);
+ if (c == null) {
+ return 0;
+ }
+ int firstVisiblePosition = getFirstVisiblePosition();
+ int top = c.getTop();
+ return -top + firstVisiblePosition * c.getHeight();
+ }
+
+}
\ No newline at end of file
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/PullToRefreshRecyclerView.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/PullToRefreshRecyclerView.java
new file mode 100644
index 0000000..d5f54bc
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/PullToRefreshRecyclerView.java
@@ -0,0 +1,63 @@
+package com.shunzhi.mychartlibrary.widget.pulltorefresh;
+
+import android.content.Context;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * Created by Mao Jiqing on 2016/9/27.
+ */
+public class PullToRefreshRecyclerView extends RecyclerView {
+ boolean allowDragBottom = true;
+ float downY = 0;
+ boolean needConsumeTouch = true;
+
+ public PullToRefreshRecyclerView(Context context) {
+ super(context);
+ }
+
+ public PullToRefreshRecyclerView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ downY = ev.getRawY();
+ needConsumeTouch = true;
+ if (getMyScrollY() == 0) {
+ allowDragBottom = true;
+ } else {
+ allowDragBottom = false;
+ }
+ } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
+ if (!needConsumeTouch) {
+ getParent().requestDisallowInterceptTouchEvent(false);
+ return false;
+ } else if (allowDragBottom) {
+ if (downY - ev.getRawY() < -2) {
+ needConsumeTouch = false;
+ getParent().requestDisallowInterceptTouchEvent(false);
+ return false;
+ }
+ }
+ }
+ getParent().requestDisallowInterceptTouchEvent(needConsumeTouch);
+ return super.dispatchTouchEvent(ev);
+ }
+
+ public int getMyScrollY() {
+ View c = getChildAt(0);
+ if (c == null) {
+ return 0;
+ }
+ int firstVisiblePosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
+ int top = c.getTop();
+ return -top + firstVisiblePosition * c.getHeight();
+ }
+
+}
\ No newline at end of file
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/WrapContentLinearLayoutManager.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/WrapContentLinearLayoutManager.java
new file mode 100644
index 0000000..b3d81b3
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/WrapContentLinearLayoutManager.java
@@ -0,0 +1,80 @@
+package com.shunzhi.mychartlibrary.widget.pulltorefresh;
+
+import android.content.Context;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Created by Mao Jiqing on 2016/9/30.
+ */
+public class WrapContentLinearLayoutManager extends LinearLayoutManager {
+ private int[] mMeasuredDimension = new int[2];
+ private int recyclerHeight = 0;
+ private RecyclerView.Recycler recycler;
+
+ public WrapContentLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
+ super(context, orientation, reverseLayout);
+ }
+
+ //... constructor
+ @Override
+ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+ try {
+ this.recycler = recycler;
+ super.onLayoutChildren(recycler, state);
+ } catch (IndexOutOfBoundsException e) {
+ Log.e("probe", "ERROR = " + e.getMessage());
+ }
+ }
+
+ public int getRecyclerHeight() {
+ if (recycler == null) {
+ return 0;
+ }
+ int height = 0;
+ for (int i = 0; i < getItemCount(); i++) {
+ measureScrapChild(recycler, i,
+ View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
+ mMeasuredDimension);
+
+ if (getOrientation() == HORIZONTAL) {
+ if (i == 0) {
+ height = mMeasuredDimension[1];
+ }
+ } else {
+ height = height + mMeasuredDimension[1];
+ }
+ }
+ recyclerHeight = height;
+ return recyclerHeight;
+ }
+
+ private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
+ int heightSpec, int[] measuredDimension) {
+ try {
+ View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException
+
+ if (view != null) {
+ RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
+
+ int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
+ getPaddingLeft() + getPaddingRight(), p.width);
+
+ int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
+ getPaddingTop() + getPaddingBottom(), p.height);
+
+ view.measure(childWidthSpec, childHeightSpec);
+ measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
+ measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
+ recycler.recycleView(view);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ }
+ }
+}
\ No newline at end of file
diff --git a/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/base/PullToRefreshView.java b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/base/PullToRefreshView.java
new file mode 100644
index 0000000..3e35ca8
--- /dev/null
+++ b/mychartlibrary/src/main/java/com/shunzhi/mychartlibrary/widget/pulltorefresh/base/PullToRefreshView.java
@@ -0,0 +1,37 @@
+package com.shunzhi.mychartlibrary.widget.pulltorefresh.base;
+
+import android.content.Context;
+import android.view.View;
+
+import com.shunzhi.mychartlibrary.widget.pulltorefresh.PullToRefreshListView;
+import com.shunzhi.mychartlibrary.widget.pulltorefresh.PullToRefreshRecyclerView;
+
+
+/**
+ * Created by Mao Jiqing on 2016/10/10.
+ */
+public class PullToRefreshView extends View {
+
+ public static final int LISTVIEW = 0;
+ public static final int RECYCLERVIEW = 1;
+
+ public PullToRefreshView(Context context) {
+ super(context);
+ }
+
+ public View getSlideView(int slideViewType) {
+ View baseView = null;
+ switch (slideViewType) {
+ case LISTVIEW:
+ baseView = new PullToRefreshListView(getContext());
+ break;
+ case RECYCLERVIEW:
+ baseView = new PullToRefreshRecyclerView(getContext());
+ break;
+ default:
+ baseView = null;
+ break;
+ }
+ return baseView;
+ }
+}
diff --git a/mychartlibrary/src/main/res/drawable/btn_back_dark_24dp.xml b/mychartlibrary/src/main/res/drawable/btn_back_dark_24dp.xml
new file mode 100644
index 0000000..062d15d
--- /dev/null
+++ b/mychartlibrary/src/main/res/drawable/btn_back_dark_24dp.xml
@@ -0,0 +1,11 @@
+
+
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/drawable/button_recording.xml b/mychartlibrary/src/main/res/drawable/button_recording.xml
new file mode 100644
index 0000000..0ae8d72
--- /dev/null
+++ b/mychartlibrary/src/main/res/drawable/button_recording.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/drawable/button_recordnormal.xml b/mychartlibrary/src/main/res/drawable/button_recordnormal.xml
new file mode 100644
index 0000000..336bcf0
--- /dev/null
+++ b/mychartlibrary/src/main/res/drawable/button_recordnormal.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/drawable/click_bg.xml b/mychartlibrary/src/main/res/drawable/click_bg.xml
new file mode 100644
index 0000000..d62509c
--- /dev/null
+++ b/mychartlibrary/src/main/res/drawable/click_bg.xml
@@ -0,0 +1,17 @@
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/drawable/item_divider_line.xml b/mychartlibrary/src/main/res/drawable/item_divider_line.xml
new file mode 100644
index 0000000..8cf968c
--- /dev/null
+++ b/mychartlibrary/src/main/res/drawable/item_divider_line.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/layout/activity_chat.xml b/mychartlibrary/src/main/res/layout/activity_chat.xml
new file mode 100644
index 0000000..496c30b
--- /dev/null
+++ b/mychartlibrary/src/main/res/layout/activity_chat.xml
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/layout/layout_action_bar.xml b/mychartlibrary/src/main/res/layout/layout_action_bar.xml
new file mode 100644
index 0000000..b352d54
--- /dev/null
+++ b/mychartlibrary/src/main/res/layout/layout_action_bar.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/layout/layout_expression_gridview.xml b/mychartlibrary/src/main/res/layout/layout_expression_gridview.xml
new file mode 100644
index 0000000..e372109
--- /dev/null
+++ b/mychartlibrary/src/main/res/layout/layout_expression_gridview.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/mychartlibrary/src/main/res/layout/layout_mess_iv_listitem.xml b/mychartlibrary/src/main/res/layout/layout_mess_iv_listitem.xml
new file mode 100644
index 0000000..c0a157d
--- /dev/null
+++ b/mychartlibrary/src/main/res/layout/layout_mess_iv_listitem.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/layout/layout_row_expression.xml b/mychartlibrary/src/main/res/layout/layout_row_expression.xml
new file mode 100644
index 0000000..1fe5ed7
--- /dev/null
+++ b/mychartlibrary/src/main/res/layout/layout_row_expression.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/mychartlibrary/src/main/res/layout/layout_tongbaobottom.xml b/mychartlibrary/src/main/res/layout/layout_tongbaobottom.xml
new file mode 100644
index 0000000..2615a1b
--- /dev/null
+++ b/mychartlibrary/src/main/res/layout/layout_tongbaobottom.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/layout/layout_view_headicon.xml b/mychartlibrary/src/main/res/layout/layout_view_headicon.xml
new file mode 100644
index 0000000..b9dd628
--- /dev/null
+++ b/mychartlibrary/src/main/res/layout/layout_view_headicon.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/layout/layout_voice_dialog_manager.xml b/mychartlibrary/src/main/res/layout/layout_voice_dialog_manager.xml
new file mode 100644
index 0000000..3331d1a
--- /dev/null
+++ b/mychartlibrary/src/main/res/layout/layout_voice_dialog_manager.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f1.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f1.gif
new file mode 100644
index 0000000..b86d9ed
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f1.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f10.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f10.gif
new file mode 100644
index 0000000..f349335
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f10.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f11.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f11.gif
new file mode 100644
index 0000000..7860ce1
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f11.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f12.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f12.gif
new file mode 100644
index 0000000..9604b79
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f12.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f13.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f13.gif
new file mode 100644
index 0000000..08aa814
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f13.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f14.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f14.gif
new file mode 100644
index 0000000..014d702
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f14.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f15.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f15.gif
new file mode 100644
index 0000000..f51f217
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f15.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f16.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f16.gif
new file mode 100644
index 0000000..ff42e74
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f16.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f17.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f17.gif
new file mode 100644
index 0000000..dd72809
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f17.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f18.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f18.gif
new file mode 100644
index 0000000..f48cecf
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f18.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f19.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f19.gif
new file mode 100644
index 0000000..7fe15cb
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f19.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f2.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f2.gif
new file mode 100644
index 0000000..2f1caf5
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f2.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f20.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f20.gif
new file mode 100644
index 0000000..eae7ea7
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f20.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f21.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f21.gif
new file mode 100644
index 0000000..0918e5a
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f21.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f22.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f22.gif
new file mode 100644
index 0000000..56d93bf
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f22.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f23.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f23.gif
new file mode 100644
index 0000000..ca8ae21
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f23.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f24.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f24.gif
new file mode 100644
index 0000000..c527f3d
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f24.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f25.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f25.gif
new file mode 100644
index 0000000..ce8e2b0
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f25.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f26.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f26.gif
new file mode 100644
index 0000000..dbe6c3b
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f26.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f27.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f27.gif
new file mode 100644
index 0000000..388927a
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f27.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f28.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f28.gif
new file mode 100644
index 0000000..f264c24
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f28.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f29.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f29.gif
new file mode 100644
index 0000000..487e8e4
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f29.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f3.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f3.gif
new file mode 100644
index 0000000..ac24c91
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f3.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f30.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f30.gif
new file mode 100644
index 0000000..5e90069
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f30.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f31.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f31.gif
new file mode 100644
index 0000000..2135059
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f31.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f32.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f32.gif
new file mode 100644
index 0000000..9a57bca
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f32.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f33.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f33.gif
new file mode 100644
index 0000000..2223019
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f33.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f34.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f34.gif
new file mode 100644
index 0000000..e7bb227
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f34.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f35.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f35.gif
new file mode 100644
index 0000000..c88a182
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f35.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f36.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f36.gif
new file mode 100644
index 0000000..85eb0e6
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f36.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f37.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f37.gif
new file mode 100644
index 0000000..4005805
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f37.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f38.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f38.gif
new file mode 100644
index 0000000..59e2388
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f38.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f39.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f39.gif
new file mode 100644
index 0000000..a8ff531
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f39.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f4.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f4.gif
new file mode 100644
index 0000000..0ea7575
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f4.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f40.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f40.gif
new file mode 100644
index 0000000..666ddf4
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f40.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f5.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f5.gif
new file mode 100644
index 0000000..85fb0b8
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f5.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f6.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f6.gif
new file mode 100644
index 0000000..58013d0
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f6.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f7.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f7.gif
new file mode 100644
index 0000000..00d6f73
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f7.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f8.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f8.gif
new file mode 100644
index 0000000..ca7e20e
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f8.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/f9.gif b/mychartlibrary/src/main/res/mipmap-hdpi/f9.gif
new file mode 100644
index 0000000..64b6925
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/f9.gif differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/ic_launcher.png b/mychartlibrary/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/ic_launcher_round.png b/mychartlibrary/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..1b52399
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/msg_state_fail_resend_pressed.png b/mychartlibrary/src/main/res/mipmap-hdpi/msg_state_fail_resend_pressed.png
new file mode 100644
index 0000000..e8f3ff5
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/msg_state_fail_resend_pressed.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-hdpi/xsearch_loading.png b/mychartlibrary/src/main/res/mipmap-hdpi/xsearch_loading.png
new file mode 100644
index 0000000..c0d172c
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-hdpi/xsearch_loading.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-xhdpi/cancel.png b/mychartlibrary/src/main/res/mipmap-xhdpi/cancel.png
new file mode 100644
index 0000000..519218c
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-xhdpi/cancel.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-xhdpi/chatting_setmode_keyboard_btn_normal.png b/mychartlibrary/src/main/res/mipmap-xhdpi/chatting_setmode_keyboard_btn_normal.png
new file mode 100644
index 0000000..7fecfb5
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-xhdpi/chatting_setmode_keyboard_btn_normal.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-xhdpi/emoji.png b/mychartlibrary/src/main/res/mipmap-xhdpi/emoji.png
new file mode 100644
index 0000000..bc518e0
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-xhdpi/emoji.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-xhdpi/tb_bottom_images.png b/mychartlibrary/src/main/res/mipmap-xhdpi/tb_bottom_images.png
new file mode 100644
index 0000000..5b264c2
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-xhdpi/tb_bottom_images.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-xhdpi/tb_bottom_offen.png b/mychartlibrary/src/main/res/mipmap-xhdpi/tb_bottom_offen.png
new file mode 100644
index 0000000..2b1d7a6
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-xhdpi/tb_bottom_offen.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-xhdpi/tb_bottom_take_photo.png b/mychartlibrary/src/main/res/mipmap-xhdpi/tb_bottom_take_photo.png
new file mode 100644
index 0000000..3de4e36
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-xhdpi/tb_bottom_take_photo.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-xhdpi/tb_more.png b/mychartlibrary/src/main/res/mipmap-xhdpi/tb_more.png
new file mode 100644
index 0000000..19ebec6
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-xhdpi/tb_more.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-xhdpi/voice_btn_normal.png b/mychartlibrary/src/main/res/mipmap-xhdpi/voice_btn_normal.png
new file mode 100644
index 0000000..6737425
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-xhdpi/voice_btn_normal.png differ
diff --git a/mychartlibrary/src/main/res/mipmap-xhdpi/voice_to_short.png b/mychartlibrary/src/main/res/mipmap-xhdpi/voice_to_short.png
new file mode 100644
index 0000000..16bc857
Binary files /dev/null and b/mychartlibrary/src/main/res/mipmap-xhdpi/voice_to_short.png differ
diff --git a/mychartlibrary/src/main/res/values/colors.xml b/mychartlibrary/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3f6376f
--- /dev/null
+++ b/mychartlibrary/src/main/res/values/colors.xml
@@ -0,0 +1,157 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+ #30ffffff
+ #000000
+ #ffffff
+ #e5e5e5
+ #f0f2f6
+ #fafafa
+ #f5f5f5
+ #f3f3f3
+ #aaaaaa
+ #f0f0f0
+ #666666
+ #f4f5f7
+ #bfbfbf
+ #f1f2f6
+ #333333
+ #999999
+ #818181
+ #051b28
+ #848689
+ #252525
+ #232326
+ #f2f4f5
+
+ #0a85ff
+ #356cb0
+ #5795f3
+ #2297fe
+
+ #edbb74
+ #ffb802
+ #fff5e5
+ #fd9c00
+ #ffdd2727
+ #c82300
+ #e64a4b
+ #ff4719
+ #f2426c
+ #ff7870
+ #dd2727
+ #ff4258
+ #e72701
+
+ #ff5757
+ #E1E0DE
+ #00000000
+ #d9d9d9
+ #e9eaec
+ #3097e6
+ #503097e6
+ #dcf4ff
+ #84d4f9
+ #45b1e3
+ #3299e8
+ #1597f2
+
+ #346cb0
+
+ #278ff3
+ #e6e6e6
+ #dedede
+ #808080
+ #7f000000
+ #48000000
+ #50000000
+ #70ffffff
+ #ebebeb
+ #bbbbbb
+ #222222
+ #626262
+ #cccccc
+ #FFE1E8EB
+ #ffb84c
+ #feeedc
+ #fadcdc
+ #2f2f2f
+ #656565
+ #595959
+ #343434
+ #4ca4fe
+ #14ccca
+ #f26464
+ #1597f2
+
+ #dddddd
+ #838383
+ #999999
+ #f2f5f5
+ #f8f8f8
+ #c9c7c3
+ #b2b2b2
+ #a3a8ad
+ #d2d2d2
+ #b3b3b3
+ #a5a5a5
+ #929292
+ #e0e0e0
+ #634444
+
+ - @color/default_text
+
+ #ffb9b9b9
+ #ff454545
+ #ff303030
+ #ffd9d9d9
+ #ffd2d2d2
+ #fff5f5f5
+ #ff464646
+ #ff252525
+ #ffffffff
+ #ffcc3131
+ #ffefefef
+ #ffd9d9d9
+ #fff9f9f9
+ #ffcdcdcd
+ #fff5f5f5
+ #ffcdcdcd
+ #ff252525
+ #ff464646
+ #ff252525
+ #ffbc494d
+ #ff2b2b2b
+ #ffcd2323
+ #ff573535
+ #30ffffff
+ #efffffff
+ #e6e0e0e0
+
+ #494947
+ #B8B8B9
+ #F0EFF5
+
+ #FC5B6A
+
+ #2956FE
+ #00CB87
+ #6d9bff
+ #ACC9FC
+ #6d9bff
+ #F0EFF5
+ #494947
+ #ABC9FF
+ #1BE2E5
+ #ACD1FB
+ #757575
+ #5FB762
+ #00000000
+ #60b3f6
+ #2b71c4
+ #80bebebe
+
+ #00000000
+
diff --git a/mychartlibrary/src/main/res/values/dimens.xml b/mychartlibrary/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..b2f3bc7
--- /dev/null
+++ b/mychartlibrary/src/main/res/values/dimens.xml
@@ -0,0 +1,101 @@
+
+
+
+ 16dp
+ 16dp
+
+
+ 4dp
+ 8dp
+ 16dp
+ 32dp
+ 64dp
+ 48dp
+ 260dp
+
+
+ 34dp
+ 36dp
+ 24dp
+ 20dp
+ 16dp
+ 14dp
+ 12dp
+
+
+ 2dp
+ 136dp
+
+ 50dp
+ 50dp
+ 6dp
+
+ 0.66dp
+ 2dp
+ 4dp
+ 5dp
+ 8dp
+ 10dp
+ 14dp
+ 16dp
+ 22dp
+ 24dp
+ 26dp
+
+ 30dp
+ 36dp
+ 40dp
+ 60dp
+ 72dp
+
+ 12sp
+ 14sp
+ 16dp
+ 18sp
+
+ 10dp
+ 12dp
+ 14dp
+ 16dp
+ 18dp
+ 20dp
+ 24dp
+ 30dp
+ 1dp
+ 2dp
+ 3dp
+ 4dp
+ 5dp
+ 6dp
+ 7dp
+ 8dp
+ 9dp
+ 10dp
+ 11dp
+ 12dp
+ 13dp
+ 14dp
+ 15dp
+ 16dp
+ 17.0dp
+ 18.0dp
+ 19dp
+ 20dp
+ 24dp
+ 25dp
+ 30dp
+ 32dp
+ 40dp
+ 50dp
+ 60dp
+ 80dp
+ 100dp
+ 110dp
+ 120dp
+ 150dp
+ 200dp
+ 250dp
+ 300dp
+ 310dp
+
+
\ No newline at end of file
diff --git a/mychartlibrary/src/main/res/values/strings.xml b/mychartlibrary/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f5b6c0d
--- /dev/null
+++ b/mychartlibrary/src/main/res/values/strings.xml
@@ -0,0 +1,10 @@
+
+ MyChartLibrary
+
+ 按住 说话
+ 松开 结束
+ 松开手指,取消发送
+ 手指上滑,取消发送
+ 录音时间过短
+ 录音时间过长
+
diff --git a/mychartlibrary/src/main/res/values/styles.xml b/mychartlibrary/src/main/res/values/styles.xml
new file mode 100644
index 0000000..a9bc584
--- /dev/null
+++ b/mychartlibrary/src/main/res/values/styles.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mychartlibrary/src/test/java/com/shunzhi/mychartlibrary/ExampleUnitTest.java b/mychartlibrary/src/test/java/com/shunzhi/mychartlibrary/ExampleUnitTest.java
new file mode 100644
index 0000000..d62f8d7
--- /dev/null
+++ b/mychartlibrary/src/test/java/com/shunzhi/mychartlibrary/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.shunzhi.mychartlibrary;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 74f0e2f..c2d8ab7 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':mvpsdk', ':roundedimageview-2.2.1', ':processor'
+include ':app', ':mvpsdk', ':roundedimageview-2.2.1', ':processor', ':mychartlibrary'
--
libgit2 0.21.0