Repository: guanpy/WhiteBoard Branch: master Commit: 12e47c0c320b Files: 91 Total size: 209.6 KB Directory structure: gitextract_yv6_nzdc/ ├── .gitignore ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── example/ │ │ └── gpy/ │ │ └── whiteboard/ │ │ └── ApplicationTest.java │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ └── gpy/ │ │ └── whiteboard/ │ │ ├── MainActivity.java │ │ ├── SelfApplication.java │ │ ├── bean/ │ │ │ └── WbInfo.java │ │ ├── utils/ │ │ │ ├── FileUtil.java │ │ │ ├── SdCardStatus.java │ │ │ ├── StoreUtil.java │ │ │ └── ToastUtils.java │ │ └── view/ │ │ ├── WhiteBoardActivity.java │ │ ├── base/ │ │ │ └── BaseActivity.java │ │ └── widget/ │ │ └── floatingactionmenu/ │ │ ├── AddFloatingActionButton.java │ │ ├── FloatingActionButton.java │ │ ├── FloatingActionsMenu.java │ │ ├── FloatingImageButton.java │ │ └── TouchDelegateGroup.java │ └── res/ │ ├── drawable/ │ │ ├── bg_white_board.xml │ │ ├── common_cancel_selector.xml │ │ ├── common_confirm_selector.xml │ │ ├── draw_text_border.xml │ │ ├── fab_label_background.xml │ │ ├── ic_back_selector.xml │ │ ├── white_board_add_selector.xml │ │ ├── white_board_color_black_selector.xml │ │ ├── white_board_color_green_selector.xml │ │ ├── white_board_color_orange_selector.xml │ │ ├── white_board_color_pink_selector.xml │ │ ├── white_board_color_purple_selector.xml │ │ ├── white_board_disable_selector.xml │ │ ├── white_board_eraser_selected_selector.xml │ │ ├── white_board_eraser_selector.xml │ │ ├── white_board_export_selector.xml │ │ ├── white_board_next_page_selector.xml │ │ ├── white_board_pen_selected_selector.xml │ │ ├── white_board_pen_selector.xml │ │ ├── white_board_pre_page_selector.xml │ │ ├── white_board_redo_selector.xml │ │ ├── white_board_save_selector.xml │ │ ├── white_board_text_bold_selected_selector.xml │ │ ├── white_board_text_bold_selector.xml │ │ ├── white_board_text_italics_selected_selector.xml │ │ ├── white_board_text_italics_selector.xml │ │ ├── white_board_text_selected_selector.xml │ │ ├── white_board_text_selector.xml │ │ ├── white_board_text_underline_selected_selector.xml │ │ ├── white_board_text_underline_selector.xml │ │ ├── white_board_undisable_selector.xml │ │ └── white_board_undo_selector.xml │ ├── layout/ │ │ ├── activity_main.xml │ │ ├── activity_white_board.xml │ │ ├── draw_text.xml │ │ └── wb_item.xml │ ├── values/ │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── values-w820dp/ │ └── dimens.xml ├── build.gradle ├── gradle.properties ├── settings.gradle └── wblib/ ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src/ ├── androidTest/ │ └── java/ │ └── com/ │ └── github/ │ └── guanpy/ │ └── wblib/ │ └── ApplicationTest.java └── main/ ├── AndroidManifest.xml ├── java/ │ └── com/ │ └── github/ │ └── guanpy/ │ └── wblib/ │ ├── bean/ │ │ ├── DrawPenPoint.java │ │ ├── DrawPenStr.java │ │ ├── DrawPoint.java │ │ ├── DrawTextPoint.java │ │ ├── Point.java │ │ ├── WhiteBoardPoint.java │ │ └── WhiteBoardPoints.java │ ├── utils/ │ │ ├── AppContextUtil.java │ │ ├── BeanUtil.java │ │ ├── Events.java │ │ ├── OperationUtils.java │ │ └── WhiteBoardVariable.java │ └── widget/ │ ├── DrawPenView.java │ ├── DrawTextLayout.java │ └── DrawTextView.java └── res/ ├── drawable/ │ └── draw_text_border.xml ├── layout/ │ └── draw_text.xml └── values/ ├── colors.xml └── strings.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ #Android generated bin gen lint.xml lint #Eclipse .project .classpath .settings .checkstyle #IntelliJ IDEA .idea *.iml *.ipr *.iws classes gen-external-apklibs #gradle .gradle local.properties gradlew gradlew.bat gradle/ build/ #vi *.swp #other editors *.bak #Maven target release.properties pom.xml.* #Ant build.xml ant.properties local.properties proguard.cfg proguard-project.txt #Other .DS_Store Thumbs.db tmp *.tgz *.lock *.lck com_crashlytics_export_strings.xml ================================================ FILE: README.md ================================================ # WhiteBoard - 1、涂鸦功能:包括画笔粗细选择、颜色选择、橡皮擦等; - 2、文字输入:包括颜色选择、增、删、改等; - 3、白板功能:包括撤销、重做、新增、锁屏、切换、保存为图片、保存到本地等功能 ![](https://github.com/guanpy/WhiteBoard/blob/master/screenshot/main.jpg) ## 预览 ![](https://github.com/guanpy/WhiteBoard/blob/master/screenshot/main_pen.gif) ![](https://github.com/guanpy/WhiteBoard/blob/master/screenshot/main_text.gif) ## TODO - 1、DrawTextView的接口化 - 2、白板数据结构重定义 - 3、markId的重新加载问题 ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.example.gpy.whiteboard" minSdkVersion 14 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':wblib') compile 'com.android.support:appcompat-v7:23.1.1' compile'com.squareup.retrofit:converter-jackson:1.9.0' compile 'com.jakewharton:butterknife:6.1.0' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in D:\software\Java\Android\sdk_studio/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: app/src/androidTest/java/com/example/gpy/whiteboard/ApplicationTest.java ================================================ package com.example.gpy.whiteboard; import android.app.Application; import android.test.ApplicationTestCase; /** * Testing Fundamentals */ public class ApplicationTest extends ApplicationTestCase { public ApplicationTest() { super(Application.class); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/MainActivity.java ================================================ package com.example.gpy.whiteboard; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.example.gpy.whiteboard.utils.FileUtil; import com.example.gpy.whiteboard.utils.StoreUtil; import com.example.gpy.whiteboard.utils.ToastUtils; import com.example.gpy.whiteboard.view.WhiteBoardActivity; import com.example.gpy.whiteboard.view.base.BaseActivity; import com.github.guanpy.wblib.utils.OperationUtils; import java.io.File; import java.util.ArrayList; import butterknife.InjectView; public class MainActivity extends BaseActivity { @InjectView(R.id.lv_wb) ListView mLv; @InjectView(R.id.iv_wb_add) ImageView mIvAdd; private WbAdapter mWbAdapter; ArrayList filenames; ArrayList filepaths; private long mBackPressedTime; @Override protected int getLayoutId() { return R.layout.activity_main; } @Override protected void afterCreate(Bundle savedInstanceState) { loadData(); initView(); } private void loadData() { File folder = new File(StoreUtil.getWbPath()); if (!folder.exists()) { folder.mkdirs(); } final File[] files = folder.listFiles(); if (files.length > 0) { filenames = new ArrayList(); filepaths = new ArrayList(); for (File f : files) { filenames.add(FileUtil.getFileName(f)); filepaths.add(f.getAbsolutePath()); } } // mWbAdapter.notifyDataSetChanged(); } private void initView() { mWbAdapter = new WbAdapter(); mLv.setAdapter(mWbAdapter); mIvAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { OperationUtils.getInstance().initDrawPointList(); navi2Page(WhiteBoardActivity.class); } }); } @Override public void onBackPressed() { final long mCurrentTime = System.currentTimeMillis(); if (mCurrentTime - this.mBackPressedTime > 1000) { ToastUtils.showToast(this, R.string.app_logout); this.mBackPressedTime = mCurrentTime; return; } super.onBackPressed(); System.exit(0); } private class WbAdapter extends BaseAdapter { @Override public int getCount() { return filenames != null ? filenames.size() : 0; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(final int position, View convertView, ViewGroup parent) { WbViewHolder holder = null; if (convertView != null) { holder = (WbViewHolder) convertView.getTag(); } else { convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.wb_item, null); if (convertView != null) { convertView.setTag( holder = new WbViewHolder(convertView) ); } if (holder != null) { holder.nWbName.setText(filenames.get(position)); convertView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { StoreUtil.readWhiteBoardPoints(filepaths.get(position)); navi2Page(WhiteBoardActivity.class); } }); } } return convertView; } } private final class WbViewHolder { final TextView nWbName; public WbViewHolder(final View view) { this.nWbName = (TextView) view.findViewById(R.id.tv_wb_name); } } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/SelfApplication.java ================================================ package com.example.gpy.whiteboard; import android.app.Application; import com.example.gpy.whiteboard.utils.SdCardStatus; import com.example.gpy.whiteboard.utils.StoreUtil; import com.github.guanpy.wblib.utils.AppContextUtil; import com.github.guanpy.wblib.utils.OperationUtils; /** * Created by gpy on 2015/8/17. */ public class SelfApplication extends Application{ @Override public void onCreate() { super.onCreate(); AppContextUtil.init(this); SdCardStatus.init(StoreUtil.CACHE_DIR); OperationUtils.getInstance().init(); } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/bean/WbInfo.java ================================================ package com.example.gpy.whiteboard.bean; import java.io.File; /** * Created by gpy on 2016/4/22. */ public class WbInfo { /** * 文件名 */ private String mName; /** * 文件 */ private File mFile; /** * 获得文件名 * Created 2016-3-22 18:30:34 * * @return 文件名 * @author gpy */ public String getName() { return mName; } /** * 设置文件名 * Created 2016-3-22 18:30:34 * * @param name 文件名 * @author gpy */ public void setName(String name) { this.mName = name; } /** * 获得文件 * Created 2016-3-22 18:30:34 * * @return 文件 * @author gpy */ public File getFile() { return mFile; } /** * 设置文件 * Created 2016-3-22 18:30:34 * * @param file 文件 * @author gpy */ public void setFile(File file) { this.mFile = file; } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/utils/FileUtil.java ================================================ package com.example.gpy.whiteboard.utils; import android.text.TextUtils; import android.util.Log; import java.io.File; import java.io.FileInputStream; import java.text.DecimalFormat; /** * Created by gpy on 2016/4/6. */ public class FileUtil { public static final int SIZETYPE_B = 1;// 获取文件大小单位为B的double值 public static final int SIZETYPE_KB = 2;// 获取文件大小单位为KB的double值 public static final int SIZETYPE_MB = 3;// 获取文件大小单位为MB的double值 public static final int SIZETYPE_GB = 4;// 获取文件大小单位为GB的double值 /** * 获取指定文件名字 * * @param file * @return */ public static String getFileName(File file) { final String name = file.getName(); if (!TextUtils.isEmpty(name)) { final int ext = name.lastIndexOf("."); if (ext > 0) { return name.substring(0, ext); } } return name; } /** * 调用此方法自动计算指定文件或指定文件夹的大小 * * @param filePath 文件路径 * @return 计算好的带B、KB、MB、GB的字符串 */ public static String getAutoFileOrFilesSize(String filePath) { File file = new File(filePath); long blockSize = 0; try { if (file.isDirectory()) { blockSize = getFileSizes(file); } else { blockSize = getFileSize(file); } } catch (Exception e) { e.printStackTrace(); Log.e("获取文件大小", "获取失败!"); } return FormetFileSize(blockSize); } /** * 获取文件指定文件的指定单位的大小 * * @param filePath 文件路径 * @param sizeType 获取大小的类型1为B、2为KB、3为MB、4为GB * @return double值的大小 */ public static double getFileOrFilesSize(String filePath, int sizeType) { File file = new File(filePath); long blockSize = 0; try { if (file.isDirectory()) { blockSize = getFileSizes(file); } else { blockSize = getFileSize(file); } } catch (Exception e) { e.printStackTrace(); Log.e("获取文件大小", "获取失败!"); } return FormetFileSize(blockSize, sizeType); } /** * 获取指定文件大小 * * @param file * @return * @throws Exception */ private static long getFileSize(File file) throws Exception { long size = 0; if (file.exists()) { FileInputStream fis = null; fis = new FileInputStream(file); size = fis.available(); } else { file.createNewFile(); Log.e("获取文件大小", "文件不存在!"); } return size; } /** * 获取指定文件夹 * * @param f * @return * @throws Exception */ private static long getFileSizes(File f) throws Exception { long size = 0; File flist[] = f.listFiles(); for (int i = 0; i < flist.length; i++) { if (flist[i].isDirectory()) { size = size + getFileSizes(flist[i]); } else { size = size + getFileSize(flist[i]); } } return size; } /** * 转换文件大小 * * @param fileS * @return */ private static String FormetFileSize(long fileS) { DecimalFormat df = new DecimalFormat("#.00"); String fileSizeString = ""; String wrongSize = "0B"; if (fileS == 0) { return wrongSize; } if (fileS < 1024) { fileSizeString = df.format((double) fileS) + "B"; } else if (fileS < 1048576) { fileSizeString = df.format((double) fileS / 1024) + "KB"; } else if (fileS < 1073741824) { fileSizeString = df.format((double) fileS / 1048576) + "MB"; } else { fileSizeString = df.format((double) fileS / 1073741824) + "GB"; } return fileSizeString; } /** * 转换文件大小,指定转换的类型 * * @param fileS * @param sizeType * @return */ private static double FormetFileSize(long fileS, int sizeType) { DecimalFormat df = new DecimalFormat("#.00"); double fileSizeLong = 0; switch (sizeType) { case SIZETYPE_B: fileSizeLong = Double.valueOf(df.format((double) fileS)); break; case SIZETYPE_KB: fileSizeLong = Double.valueOf(df.format((double) fileS / 1024)); break; case SIZETYPE_MB: fileSizeLong = Double.valueOf(df.format((double) fileS / 1048576)); break; case SIZETYPE_GB: fileSizeLong = Double.valueOf(df .format((double) fileS / 1073741824)); break; default: break; } return fileSizeLong; } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/utils/SdCardStatus.java ================================================ package com.example.gpy.whiteboard.utils; import android.os.Environment; import java.io.File; /** * Created by gpy on 2015/10/20. */ public class SdCardStatus { private static String CACHE_FOLDER_NAME; private static String NONE_SD_CARD_PROMPT = "您的手机中sd卡不存在"; public static void init(String cacheFolderName) { CACHE_FOLDER_NAME = cacheFolderName; } public static String getDefaulstCacheDirInSdCard() throws IllegalStateException { String sdCardPath = null; sdCardPath = getSDPath(); if (null == sdCardPath) { throw new IllegalStateException(NONE_SD_CARD_PROMPT); } return sdCardPath + File.separator + CACHE_FOLDER_NAME; } /** * when not exist sd card,return null. * * @return */ public static String getSDPath() { boolean sdCardExist = Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED); if (sdCardExist) { return Environment.getExternalStorageDirectory().getAbsolutePath(); } else { return null; } } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/utils/StoreUtil.java ================================================ package com.example.gpy.whiteboard.utils; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.text.TextUtils; import android.util.Log; import com.example.gpy.whiteboard.R; import com.github.guanpy.wblib.bean.DrawPenPoint; import com.github.guanpy.wblib.bean.DrawPenStr; import com.github.guanpy.wblib.bean.DrawPoint; import com.github.guanpy.wblib.bean.Point; import com.github.guanpy.wblib.bean.WhiteBoardPoint; import com.github.guanpy.wblib.bean.WhiteBoardPoints; import com.github.guanpy.wblib.utils.AppContextUtil; import com.github.guanpy.wblib.utils.OperationUtils; import com.google.gson.Gson; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.UUID; /** * 存储空间工具类 * Created by gpy on 2015/8/2. */ public class StoreUtil { private static final String TAG = "StoreUtil"; private static final String CHARSET = "UTF-8"; public static final String CACHE_DIR = "WhiteBoard"; private static final String CACHE_DIR_PHOTO = "photo"; private static final String PHOTO_FORMAT_PNG = ".png"; private static final String CACHE_DIR_WB = "wb"; private static final String WB_FORMAT = ".wb"; /** * 获取保存路径 */ public static String getPhotoSavePath() { return getPhotoPath() + File.separator + UUID.randomUUID().toString() + PHOTO_FORMAT_PNG; } public static String getPhotoPath() { return SdCardStatus.getDefaulstCacheDirInSdCard() + File.separator + CACHE_DIR_PHOTO; } /** * 获取保存路径 */ public static String getWbSavePath() { return getWbPath() + File.separator + UUID.randomUUID().toString() + WB_FORMAT; } public static String getWbPath() { return SdCardStatus.getDefaulstCacheDirInSdCard() + File.separator + CACHE_DIR_WB; } /** * 存储白板集 */ public static void saveWhiteBoardPoints() { WhiteBoardPoints whiteBoardPoints = OperationUtils.getInstance().getWhiteBoardPoints(); if (whiteBoardPoints == null || whiteBoardPoints.getWhiteBoardPoints() == null || whiteBoardPoints.getWhiteBoardPoints().isEmpty()) { return; } for (WhiteBoardPoint whiteBoardPoint : whiteBoardPoints.getWhiteBoardPoints()) {//清除绘画路径,保留字符串形式就行 for (DrawPoint drawPoint : whiteBoardPoint.getSavePoints()) { if (drawPoint.getType() == OperationUtils.DRAW_PEN) { drawPoint.setDrawPen(null); } } } String strJson = new Gson().toJson(whiteBoardPoints); StoreUtil.write(strJson, getWbSavePath()); convertWhiteBoardPoints(whiteBoardPoints); OperationUtils.getInstance().setWhiteBoardPoints(whiteBoardPoints); ToastUtils.showToast(AppContextUtil.getContext(), AppContextUtil.getContext().getString(R.string.white_board_save_sucess)); } /** * 读取白板集 */ public static void readWhiteBoardPoints(String filePath) { String strJson = StoreUtil.read(filePath); if (!TextUtils.isEmpty(strJson)) { WhiteBoardPoints whiteBoardPoints = new Gson().fromJson(strJson, WhiteBoardPoints.class); convertWhiteBoardPoints(whiteBoardPoints); OperationUtils.getInstance().setWhiteBoardPoints(whiteBoardPoints); } } /** * 从json字符中将Path、Paint系统类转换出来 */ public static void convertWhiteBoardPoints(WhiteBoardPoints whiteBoardPoints) { for (WhiteBoardPoint whiteBoardPoint : whiteBoardPoints.getWhiteBoardPoints()) { whiteBoardPoint.getDeletePoints().clear(); for (DrawPoint drawPoint : whiteBoardPoint.getSavePoints()) { if (drawPoint.getType() == OperationUtils.DRAW_PEN) { DrawPenStr drawPenStr = drawPoint.getDrawPenStr(); Paint paint = new Paint(); paint.setAntiAlias(true);//是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢 paint.setDither(true);// 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰 paint.setColor(drawPenStr.getColor());//设置绘制的颜色 paint.setStyle(Paint.Style.STROKE);//设置画笔的样式 paint.setStrokeJoin(Paint.Join.ROUND);//设置绘制时各图形的结合方式,如平滑效果等 paint.setStrokeCap(Paint.Cap.ROUND);//当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式 Cap.ROUND,或方形样式Cap.SQUARE paint.setStrokeWidth(drawPenStr.getStrokeWidth());//当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度 if (drawPenStr.getIsEraser()) { paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));//擦除模式 } Path path = new Path(); path.moveTo(drawPenStr.getMoveTo().getX(), drawPenStr.getMoveTo().getY()); for (int i = 0; i < drawPenStr.getQuadToA().size(); i++) { Point pointA = drawPenStr.getQuadToA().get(i); Point pointB = drawPenStr.getQuadToB().get(i); path.quadTo(pointA.getX(), pointA.getY(), pointB.getX(), pointB.getY()); } path.lineTo(drawPenStr.getLineTo().getX(), drawPenStr.getLineTo().getY()); path.offset(drawPenStr.getOffset().getX(), drawPenStr.getOffset().getY()); DrawPenPoint drawPenPoint = new DrawPenPoint(); drawPenPoint.setPaint(paint); drawPenPoint.setPath(path); drawPoint.setDrawPen(drawPenPoint); } } } } /** * 保存内容到文件中 */ public static void write(String strWb, String path) { if (TextUtils.isEmpty(strWb) || TextUtils.isEmpty(path)) { Log.d(TAG, "Trying to save null or 0 length strWb or path"); return; } File toFile = new File(path); if (!toFile.getParentFile().exists()) { toFile.getParentFile().mkdirs(); } if (toFile.exists()) { toFile.delete(); } try { toFile.createNewFile(); } catch (IOException e) { Log.e(TAG, "IOException:" + e.getMessage()); toFile = null; } finally { if (null != toFile && null != strWb) { OutputStream outStream = null; try { outStream = new FileOutputStream(toFile); } catch (FileNotFoundException e) { Log.d(TAG, "FileNotFoundException:" + e.getMessage()); outStream = null; } finally { if (null != outStream) { try { outStream.write(strWb.getBytes("utf-8")); outStream.flush(); } catch (IOException e) { Log.e(TAG, "IOException:" + e.getMessage()); } finally { try { if (null != outStream) { outStream.close(); } } catch (IOException e) { Log.d(TAG, "IOException" + e.getMessage()); } } } } } } } /** * 加载文件内容 */ public static String read(String wbPath) { File file = new File(wbPath); if (file.exists()) { FileInputStream fis = null; try { fis = new FileInputStream(file); int len = fis.available(); if (len > 0) { byte[] buf = new byte[len]; fis.read(buf); String string = new String(buf, CHARSET); return string; } } catch (Exception e) { e.printStackTrace(); } catch (OutOfMemoryError e) { e.printStackTrace(); } finally { if (fis != null) try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/utils/ToastUtils.java ================================================ package com.example.gpy.whiteboard.utils; import android.content.Context; import android.widget.Toast; /** * Created by gpy on 2016/4/11. */ public class ToastUtils { public static void showToast(Context context, String string) { Toast.makeText(context, string, Toast.LENGTH_SHORT).show(); } public static void showToast(Context context, int intStr) { Toast.makeText(context, intStr, Toast.LENGTH_SHORT).show(); } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/view/WhiteBoardActivity.java ================================================ package com.example.gpy.whiteboard.view; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import com.example.gpy.whiteboard.R; import com.example.gpy.whiteboard.utils.StoreUtil; import com.example.gpy.whiteboard.view.base.BaseActivity; import com.example.gpy.whiteboard.view.widget.floatingactionmenu.FloatingActionsMenu; import com.example.gpy.whiteboard.view.widget.floatingactionmenu.FloatingImageButton; import com.github.guanpy.library.ann.ReceiveEvents; import com.github.guanpy.wblib.bean.DrawPoint; import com.github.guanpy.wblib.utils.Events; import com.github.guanpy.wblib.utils.OperationUtils; import com.github.guanpy.wblib.utils.WhiteBoardVariable; import com.github.guanpy.wblib.widget.DrawPenView; import com.github.guanpy.wblib.widget.DrawTextLayout; import com.github.guanpy.wblib.widget.DrawTextView; import java.io.File; import java.io.FileOutputStream; import butterknife.InjectView; /** * 白板工具 * Created by gpy on 2015/6/2. */ public class WhiteBoardActivity extends BaseActivity implements View.OnClickListener { @InjectView(R.id.iv_white_board_book) ImageView mIvWhiteBoardBook; @InjectView(R.id.rl_head) RelativeLayout mRlHead; @InjectView(R.id.iv_white_board_back) ImageView mIvWhiteBoardBack; @InjectView(R.id.tv_white_board_head) TextView mTvWhiteBoardHead; @InjectView(R.id.iv_white_board_export) ImageView mIvWhiteBoardExport; @InjectView(R.id.iv_white_board_save) ImageView mIvWhiteBoardSave; @InjectView(R.id.fl_view) FrameLayout mFlView; @InjectView(R.id.db_view) DrawPenView mDbView; @InjectView(R.id.dt_view) DrawTextLayout mDtView; @InjectView(R.id.fab_menu_size) FloatingActionsMenu mFabMenuSize; @InjectView(R.id.bt_size_large) FloatingImageButton mBtSizeLarge; @InjectView(R.id.bt_size_middle) FloatingImageButton mBtSizeMiddle; @InjectView(R.id.bt_size_mini) FloatingImageButton mBtSizeMini; @InjectView(R.id.fab_menu_color) FloatingActionsMenu mFabMenuColor; @InjectView(R.id.bt_color_green) FloatingImageButton mBtColorGreen; @InjectView(R.id.bt_color_purple) FloatingImageButton mBtColorPurple; @InjectView(R.id.bt_color_pink) FloatingImageButton mBtColorPink; @InjectView(R.id.bt_color_orange) FloatingImageButton mBtColorOrange; @InjectView(R.id.bt_color_black) FloatingImageButton mBtColorBlack; @InjectView(R.id.fab_menu_text) FloatingActionsMenu mFabMenuText; @InjectView(R.id.bt_text_underline) FloatingImageButton mBtTextUnderline; @InjectView(R.id.bt_text_italics) FloatingImageButton mBtTextItalics; @InjectView(R.id.bt_text_bold) FloatingImageButton mBtTextBold; @InjectView(R.id.fab_menu_eraser) FloatingActionsMenu mFabMenuEraser; @InjectView(R.id.bt_eraser_large) FloatingImageButton mBtEraserLarge; @InjectView(R.id.bt_eraser_middle) FloatingImageButton mBtEraserMiddle; @InjectView(R.id.bt_eraser_mini) FloatingImageButton mBtEraserMini; @InjectView(R.id.iv_white_board_undo) ImageView mIvWhiteBoardUndo; @InjectView(R.id.iv_white_board_redo) ImageView mIvWhiteBoardRedo; @InjectView(R.id.ll_white_board_page) LinearLayout mLlWhiteBoardPage; @InjectView(R.id.iv_white_board_pre) ImageView mIvWhiteBoardPre; @InjectView(R.id.tv_white_board_page) TextView mTvWhiteBoardPage; @InjectView(R.id.iv_white_board_next) ImageView mIvWhiteBoardNext; @InjectView(R.id.iv_white_board_add) ImageView mIvWhiteBoardAdd; @InjectView(R.id.iv_white_board_disable) ImageView mIvWhiteBoardDisable; @InjectView(R.id.iv_white_board_quit) ImageView mIvWhiteBoardQuit; @InjectView(R.id.iv_white_board_confirm) ImageView mIvWhiteBoardConfirm; @InjectView(R.id.rl_content) RelativeLayout mRlContent; @InjectView(R.id.rl_bottom) RelativeLayout mRlBottom; @InjectView(R.id.v_bottom_back) View mVBottomBack; @InjectView(R.id.ll_white_board_pre) LinearLayout mLlWhiteBoardPre; @InjectView(R.id.ll_white_board_next) LinearLayout mLlWhiteBoardNext; @Override protected void afterCreate(Bundle bundle) { initView(); initEvent(); } private void initView() { changePenBack(); changeColorBack(); changeEraserBack(); ToolsOperation(WhiteBoardVariable.Operation.PEN_NORMAL); mDbView.post(new Runnable() { @Override public void run() { showPoints(); } }); } private void initEvent() { //头部 mIvWhiteBoardBack.setOnClickListener(this); mIvWhiteBoardExport.setOnClickListener(this); mIvWhiteBoardSave.setOnClickListener(this); mIvWhiteBoardQuit.setOnClickListener(this); mIvWhiteBoardConfirm.setOnClickListener(this); mVBottomBack.setOnClickListener(this); //画笔尺寸大小 mFabMenuSize.setOnFloatingActionsMenuClickListener(new FloatingActionsMenu.OnFloatingActionsMenuClickListener() { @Override public void addButtonLister() { ToolsOperation(WhiteBoardVariable.Operation.PEN_CLICK); } }); mBtSizeLarge.setOnClickListener(this); mBtSizeMiddle.setOnClickListener(this); mBtSizeMini.setOnClickListener(this); //画笔或者文字颜色 mFabMenuColor.setOnFloatingActionsMenuClickListener(new FloatingActionsMenu.OnFloatingActionsMenuClickListener() { @Override public void addButtonLister() { ToolsOperation(WhiteBoardVariable.Operation.COLOR_CLICK); } }); mBtColorGreen.setOnClickListener(this); mBtColorPurple.setOnClickListener(this); mBtColorPink.setOnClickListener(this); mBtColorOrange.setOnClickListener(this); mBtColorBlack.setOnClickListener(this); //文字样式 mFabMenuText.setOnFloatingActionsMenuClickListener(new FloatingActionsMenu.OnFloatingActionsMenuClickListener() { @Override public void addButtonLister() { ToolsOperation(WhiteBoardVariable.Operation.TEXT_CLICK); } }); mBtTextUnderline.setOnClickListener(this); mBtTextItalics.setOnClickListener(this); mBtTextBold.setOnClickListener(this); //橡皮擦尺寸大小 mFabMenuEraser.setOnFloatingActionsMenuClickListener(new FloatingActionsMenu.OnFloatingActionsMenuClickListener() { @Override public void addButtonLister() { ToolsOperation(WhiteBoardVariable.Operation.ERASER_CLICK); } }); mBtEraserLarge.setOnClickListener(this); mBtEraserMiddle.setOnClickListener(this); mBtEraserMini.setOnClickListener(this); mIvWhiteBoardUndo.setOnClickListener(this); mIvWhiteBoardRedo.setOnClickListener(this); mLlWhiteBoardPre.setOnClickListener(this); mIvWhiteBoardPre.setOnClickListener(this); mLlWhiteBoardNext.setOnClickListener(this); mIvWhiteBoardNext.setOnClickListener(this); mIvWhiteBoardAdd.setOnClickListener(this); mIvWhiteBoardDisable.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.iv_white_board_back://返回键 onBackPressed(); break; case R.id.iv_white_board_quit://退出文字编辑 afterEdit(false); break; case R.id.iv_white_board_confirm://保存文字编辑 afterEdit(true); break; case R.id.iv_white_board_export://保存白板操作集到本地 ToolsOperation(WhiteBoardVariable.Operation.OUTSIDE_CLICK); StoreUtil.saveWhiteBoardPoints(); break; case R.id.iv_white_board_save://保存白板为图片 ToolsOperation(WhiteBoardVariable.Operation.OUTSIDE_CLICK); saveImage(); break; case R.id.v_bottom_back://点击挡板 ToolsOperation(WhiteBoardVariable.Operation.OUTSIDE_CLICK); break; case R.id.bt_size_large://设置画笔尺寸-大号 setPenSize(WhiteBoardVariable.PenSize.LARRGE); break; case R.id.bt_size_middle://设置画笔尺寸-中号 setPenSize(WhiteBoardVariable.PenSize.MIDDLE); break; case R.id.bt_size_mini://设置画笔尺寸-小号 setPenSize(WhiteBoardVariable.PenSize.MINI); break; case R.id.bt_color_green://设置颜色-绿色 setColor(WhiteBoardVariable.Color.GREEN); break; case R.id.bt_color_purple://设置颜色-紫色 setColor(WhiteBoardVariable.Color.PURPLE); break; case R.id.bt_color_pink://设置颜色-粉色 setColor(WhiteBoardVariable.Color.PINK); break; case R.id.bt_color_orange://设置颜色-橙色 setColor(WhiteBoardVariable.Color.ORANGE); break; case R.id.bt_color_black://设置颜色-黑色 setColor(WhiteBoardVariable.Color.BLACK); break; case R.id.bt_text_underline://设置文字样式-下划线 setTextStyle(WhiteBoardVariable.TextStyle.CHANGE_UNDERLINE); break; case R.id.bt_text_italics://设置文字样式-斜体 setTextStyle(WhiteBoardVariable.TextStyle.CHANGE_ITALICS); break; case R.id.bt_text_bold://设置文字样式-粗体 setTextStyle(WhiteBoardVariable.TextStyle.CHANGE_BOLD); break; case R.id.bt_eraser_large://设置橡皮擦尺寸-大号 setEraserSize(WhiteBoardVariable.EraserSize.LARRGE); break; case R.id.bt_eraser_middle://设置橡皮擦尺寸-中号 setEraserSize(WhiteBoardVariable.EraserSize.MIDDLE); break; case R.id.bt_eraser_mini://设置橡皮擦尺寸-小号 setEraserSize(WhiteBoardVariable.EraserSize.MINI); break; case R.id.iv_white_board_undo://撤销 ToolsOperation(WhiteBoardVariable.Operation.OUTSIDE_CLICK); if (OperationUtils.getInstance().DISABLE) { undo(); } break; case R.id.iv_white_board_redo://重做 ToolsOperation(WhiteBoardVariable.Operation.OUTSIDE_CLICK); if (OperationUtils.getInstance().DISABLE) { redo(); } break; case R.id.ll_white_board_pre: case R.id.iv_white_board_pre://上一页 ToolsOperation(WhiteBoardVariable.Operation.OUTSIDE_CLICK); prePage(); break; case R.id.ll_white_board_next: case R.id.iv_white_board_next://下一页 ToolsOperation(WhiteBoardVariable.Operation.OUTSIDE_CLICK); nextPage(); break; case R.id.iv_white_board_add://新建白板 ToolsOperation(WhiteBoardVariable.Operation.OUTSIDE_CLICK); newPage(); break; case R.id.iv_white_board_disable://禁止/解禁按钮 ToolsOperation(WhiteBoardVariable.Operation.OUTSIDE_CLICK); if (OperationUtils.getInstance().DISABLE) { OperationUtils.getInstance().DISABLE = false; mIvWhiteBoardDisable.setImageResource(R.drawable.white_board_undisable_selector); mRlBottom.setVisibility(View.GONE); } else { OperationUtils.getInstance().DISABLE = true; mIvWhiteBoardDisable.setImageResource(R.drawable.white_board_disable_selector); mRlBottom.setVisibility(View.VISIBLE); } break; } } /** * 设置画笔尺寸 */ private void setPenSize(int size) { OperationUtils.getInstance().mCurrentPenSize = size; changePenBack(); mDbView.setPenSize(); } /** * 切换画笔尺寸按按钮背景 */ private void changePenBack() { if (OperationUtils.getInstance().mCurrentPenSize == WhiteBoardVariable.PenSize.LARRGE) { mBtSizeLarge.drawCircleAndRing(WhiteBoardVariable.PenSize.LARRGE, OperationUtils.getInstance().mCurrentColor); mBtSizeMiddle.drawCircle(WhiteBoardVariable.PenSize.MIDDLE); mBtSizeMini.drawCircle(WhiteBoardVariable.PenSize.MINI); } else if (OperationUtils.getInstance().mCurrentPenSize == WhiteBoardVariable.PenSize.MIDDLE) { mBtSizeLarge.drawCircle(WhiteBoardVariable.PenSize.LARRGE); mBtSizeMiddle.drawCircleAndRing(WhiteBoardVariable.PenSize.MIDDLE, OperationUtils.getInstance().mCurrentColor); mBtSizeMini.drawCircle(WhiteBoardVariable.PenSize.MINI); } else if (OperationUtils.getInstance().mCurrentPenSize == WhiteBoardVariable.PenSize.MINI) { mBtSizeLarge.drawCircle(WhiteBoardVariable.PenSize.LARRGE); mBtSizeMiddle.drawCircle(WhiteBoardVariable.PenSize.MIDDLE); mBtSizeMini.drawCircleAndRing(WhiteBoardVariable.PenSize.MINI, OperationUtils.getInstance().mCurrentColor); } } /** * 设置颜色 */ private void setColor(int color) { OperationUtils.getInstance().mCurrentColor = color; changeColorBack(); setPenSize(OperationUtils.getInstance().mCurrentPenSize); mDbView.setPenColor(); mDtView.setTextColor(); } /** * 切换颜色控制按钮背景 */ private void changeColorBack() { if (OperationUtils.getInstance().mCurrentColor == WhiteBoardVariable.Color.BLACK) { mFabMenuColor.setAddButtonBackground(R.drawable.white_board_color_black_selector); } else if (OperationUtils.getInstance().mCurrentColor == WhiteBoardVariable.Color.ORANGE) { mFabMenuColor.setAddButtonBackground(R.drawable.white_board_color_orange_selector); } else if (OperationUtils.getInstance().mCurrentColor == WhiteBoardVariable.Color.PINK) { mFabMenuColor.setAddButtonBackground(R.drawable.white_board_color_pink_selector); } else if (OperationUtils.getInstance().mCurrentColor == WhiteBoardVariable.Color.PURPLE) { mFabMenuColor.setAddButtonBackground(R.drawable.white_board_color_purple_selector); } else if (OperationUtils.getInstance().mCurrentColor == WhiteBoardVariable.Color.GREEN) { mFabMenuColor.setAddButtonBackground(R.drawable.white_board_color_green_selector); } } /** * 设置文字风格 */ private void setTextStyle(int textStyle) { mDtView.setTextStyle(textStyle); changeTextBack(); } /** * 切换文字相关按钮背景 */ private void changeTextBack() { int size = OperationUtils.getInstance().getSavePoints().size(); if (size < 1) { return; } DrawPoint dp = OperationUtils.getInstance().getSavePoints().get(size - 1); if (dp.getType() != OperationUtils.DRAW_TEXT) { return; } if (dp.getDrawText().getIsUnderline()) { mBtTextUnderline.setBackgroundResource(R.drawable.white_board_text_underline_selected_selector); } else { mBtTextUnderline.setBackgroundResource(R.drawable.white_board_text_underline_selector); } if (dp.getDrawText().getIsItalics()) { mBtTextItalics.setBackgroundResource(R.drawable.white_board_text_italics_selected_selector); } else { mBtTextItalics.setBackgroundResource(R.drawable.white_board_text_italics_selector); } if (dp.getDrawText().getIsBold()) { mBtTextBold.setBackgroundResource(R.drawable.white_board_text_bold_selected_selector); } else { mBtTextBold.setBackgroundResource(R.drawable.white_board_text_bold_selector); } } /** * 设置橡皮擦尺寸 */ private void setEraserSize(int size) { OperationUtils.getInstance().mCurrentEraserSize = size; changeEraserBack(); mDbView.setEraserSize(); } /** * 切换橡皮擦尺寸按钮背景 */ private void changeEraserBack() { if (OperationUtils.getInstance().mCurrentEraserSize == WhiteBoardVariable.EraserSize.LARRGE) { mBtEraserLarge.drawCircleAndRing(WhiteBoardVariable.EraserSize.LARRGE, WhiteBoardVariable.Color.BLACK); mBtEraserMiddle.drawCircle(WhiteBoardVariable.EraserSize.MIDDLE, WhiteBoardVariable.Color.BLACK); mBtEraserMini.drawCircle(WhiteBoardVariable.EraserSize.MINI, WhiteBoardVariable.Color.BLACK); } else if (OperationUtils.getInstance().mCurrentEraserSize == WhiteBoardVariable.EraserSize.MIDDLE) { mBtEraserLarge.drawCircle(WhiteBoardVariable.EraserSize.LARRGE, WhiteBoardVariable.Color.BLACK); mBtEraserMiddle.drawCircleAndRing(WhiteBoardVariable.EraserSize.MIDDLE, WhiteBoardVariable.Color.BLACK); mBtEraserMini.drawCircle(WhiteBoardVariable.EraserSize.MINI, WhiteBoardVariable.Color.BLACK); } else if (OperationUtils.getInstance().mCurrentEraserSize == WhiteBoardVariable.EraserSize.MINI) { mBtEraserLarge.drawCircle(WhiteBoardVariable.EraserSize.LARRGE, WhiteBoardVariable.Color.BLACK); mBtEraserMiddle.drawCircle(WhiteBoardVariable.EraserSize.MIDDLE, WhiteBoardVariable.Color.BLACK); mBtEraserMini.drawCircleAndRing(WhiteBoardVariable.EraserSize.MINI, WhiteBoardVariable.Color.BLACK); } } /** * 新建白板 */ private void newPage() { OperationUtils.getInstance().newPage(); showPoints(); } /** * 上一页 */ private void prePage() { if (OperationUtils.getInstance().mCurrentIndex > 0) { OperationUtils.getInstance().mCurrentIndex--; showPoints(); } } /** * 下一页 */ private void nextPage() { if (OperationUtils.getInstance().mCurrentIndex + 1 < OperationUtils.getInstance().getDrawPointSize()) { OperationUtils.getInstance().mCurrentIndex++; showPoints(); } } /** * 重新显示白板 */ private void showPoints() { mDbView.showPoints(); mDtView.showPoints(); mTvWhiteBoardPage.setText("" + (OperationUtils.getInstance().mCurrentIndex + 1) + "/" + OperationUtils.getInstance().getDrawPointSize()); showPage(); showUndoRedo(); } /** * 显示上下页是否可点击 */ private void showPage() { if (OperationUtils.getInstance().mCurrentIndex + 1 == OperationUtils.getInstance().getDrawPointSize()) { mIvWhiteBoardNext.setImageResource(R.drawable.white_board_next_page_click); } else { mIvWhiteBoardNext.setImageResource(R.drawable.white_board_next_page_selector); } if (OperationUtils.getInstance().mCurrentIndex == 0) { mIvWhiteBoardPre.setImageResource(R.drawable.white_board_pre_page_click); } else { mIvWhiteBoardPre.setImageResource(R.drawable.white_board_pre_page_selector); } } /** * 撤销 */ private void undo() { int size = OperationUtils.getInstance().getSavePoints().size(); if (size == 0) { return; } else { OperationUtils.getInstance().getDeletePoints().add(OperationUtils.getInstance().getSavePoints().get(size - 1)); OperationUtils.getInstance().getSavePoints().remove(size - 1); size = OperationUtils.getInstance().getDeletePoints().size(); if (OperationUtils.getInstance().getDeletePoints().get(size - 1).getType() == OperationUtils.DRAW_PEN) { mDbView.undo(); } else if (OperationUtils.getInstance().getDeletePoints().get(size - 1).getType() == OperationUtils.DRAW_TEXT) { mDtView.undo(); } showUndoRedo(); } } /** * 重做 */ private void redo() { int size = OperationUtils.getInstance().getDeletePoints().size(); if (size == 0) { return; } else { OperationUtils.getInstance().getSavePoints().add(OperationUtils.getInstance().getDeletePoints().get(size - 1)); OperationUtils.getInstance().getDeletePoints().remove(size - 1); size = OperationUtils.getInstance().getSavePoints().size(); if (OperationUtils.getInstance().getSavePoints().get(size - 1).getType() == OperationUtils.DRAW_PEN) { mDbView.redo(); } else if (OperationUtils.getInstance().getSavePoints().get(size - 1).getType() == OperationUtils.DRAW_TEXT) { mDtView.redo(); } showUndoRedo(); } } @Override protected int getLayoutId() { return R.layout.activity_white_board; } /** * 文字编辑之后 */ private void afterEdit(boolean isSave) { mIvWhiteBoardBack.setVisibility(View.VISIBLE); mIvWhiteBoardExport.setVisibility(View.VISIBLE); mIvWhiteBoardSave.setVisibility(View.VISIBLE); mRlBottom.setVisibility(View.VISIBLE); mIvWhiteBoardDisable.setVisibility(View.VISIBLE); // mLayoutParams = (RelativeLayout.LayoutParams) mRlContent.getLayoutParams(); // mLayoutParams.setMargins(OperationUtils.dip2px(24), 0, OperationUtils.dip2px(24), OperationUtils.dip2px(24)); // mRlContent.setLayoutParams(mLayoutParams); mIvWhiteBoardQuit.setVisibility(View.GONE); mIvWhiteBoardConfirm.setVisibility(View.GONE); mDbView.showPoints(); mDtView.afterEdit(isSave); } /** * 白板工具栏点击切换操作 */ private void ToolsOperation(int currentOperation) { setPenOperation(currentOperation); setColorOperation(currentOperation); setTextOperation(currentOperation); setEraserOperation(currentOperation); showOutSideView(); } /** * 显示挡板 */ private void showOutSideView() { new Handler().postDelayed(new Runnable() { @Override public void run() { if (OperationUtils.getInstance().mCurrentOPerationPen == WhiteBoardVariable.Operation.PEN_EXPAND || OperationUtils.getInstance().mCurrentOPerationColor == WhiteBoardVariable.Operation.COLOR_EXPAND || OperationUtils.getInstance().mCurrentOPerationText == WhiteBoardVariable.Operation.TEXT_EXPAND || OperationUtils.getInstance().mCurrentOPerationEraser == WhiteBoardVariable.Operation.ERASER_EXPAND) { mVBottomBack.setVisibility(View.VISIBLE); } else { mVBottomBack.setVisibility(View.GONE); } } }, 100); } /** * 白板工具栏点击切换操作-画笔 */ private void setPenOperation(int currentOperation) { switch (currentOperation) { case WhiteBoardVariable.Operation.PEN_CLICK: switch (OperationUtils.getInstance().mCurrentOPerationPen) { case WhiteBoardVariable.Operation.PEN_NORMAL: OperationUtils.getInstance().mCurrentDrawType = OperationUtils.DRAW_PEN; mDbView.setPaint(null); mFabMenuSize.setAddButtonBackground(R.drawable.white_board_pen_selected_selector); OperationUtils.getInstance().mCurrentOPerationPen = WhiteBoardVariable.Operation.PEN_CLICK; break; case WhiteBoardVariable.Operation.PEN_CLICK: mFabMenuSize.expand(); changePenBack(); OperationUtils.getInstance().mCurrentOPerationPen = WhiteBoardVariable.Operation.PEN_EXPAND; break; case WhiteBoardVariable.Operation.PEN_EXPAND: mFabMenuSize.collapse(); OperationUtils.getInstance().mCurrentOPerationPen = WhiteBoardVariable.Operation.PEN_CLICK; break; } break; case WhiteBoardVariable.Operation.TEXT_CLICK: case WhiteBoardVariable.Operation.ERASER_CLICK: switch (OperationUtils.getInstance().mCurrentOPerationPen) { case WhiteBoardVariable.Operation.PEN_NORMAL: break; case WhiteBoardVariable.Operation.PEN_CLICK: mFabMenuSize.clearDraw(); mFabMenuSize.setAddButtonBackground(R.drawable.white_board_pen_selector); OperationUtils.getInstance().mCurrentOPerationPen = WhiteBoardVariable.Operation.PEN_NORMAL; break; case WhiteBoardVariable.Operation.PEN_EXPAND: mFabMenuSize.collapse(); mFabMenuSize.clearDraw(); mFabMenuSize.setAddButtonBackground(R.drawable.white_board_pen_selector); OperationUtils.getInstance().mCurrentOPerationPen = WhiteBoardVariable.Operation.PEN_NORMAL; break; } break; case WhiteBoardVariable.Operation.COLOR_CLICK: case WhiteBoardVariable.Operation.OUTSIDE_CLICK: switch (OperationUtils.getInstance().mCurrentOPerationPen) { case WhiteBoardVariable.Operation.PEN_NORMAL: break; case WhiteBoardVariable.Operation.PEN_CLICK: break; case WhiteBoardVariable.Operation.PEN_EXPAND: mFabMenuSize.collapse(); OperationUtils.getInstance().mCurrentOPerationPen = WhiteBoardVariable.Operation.PEN_CLICK; break; } break; } } /** * 白板工具栏点击切换操作-颜色 */ private void setColorOperation(int currentOperation) { switch (currentOperation) { case WhiteBoardVariable.Operation.PEN_CLICK: case WhiteBoardVariable.Operation.TEXT_CLICK: case WhiteBoardVariable.Operation.ERASER_CLICK: case WhiteBoardVariable.Operation.OUTSIDE_CLICK: switch (OperationUtils.getInstance().mCurrentOPerationColor) { case WhiteBoardVariable.Operation.COLOR_NORMAL: break; case WhiteBoardVariable.Operation.COLOR_EXPAND: mFabMenuColor.collapse(); OperationUtils.getInstance().mCurrentOPerationColor = WhiteBoardVariable.Operation.COLOR_NORMAL; break; } break; case WhiteBoardVariable.Operation.COLOR_CLICK: switch (OperationUtils.getInstance().mCurrentOPerationColor) { case WhiteBoardVariable.Operation.COLOR_NORMAL: mFabMenuColor.expand(); OperationUtils.getInstance().mCurrentOPerationColor = WhiteBoardVariable.Operation.COLOR_EXPAND; break; case WhiteBoardVariable.Operation.COLOR_EXPAND: mFabMenuColor.collapse(); OperationUtils.getInstance().mCurrentOPerationColor = WhiteBoardVariable.Operation.COLOR_NORMAL; break; } break; } } /** * 白板工具栏点击切换操作-文字 */ private void setTextOperation(int currentOperation) { switch (currentOperation) { case WhiteBoardVariable.Operation.TEXT_CLICK: switch (OperationUtils.getInstance().mCurrentOPerationText) { case WhiteBoardVariable.Operation.TEXT_NORMAL: OperationUtils.getInstance().mCurrentDrawType = OperationUtils.DRAW_TEXT; mFabMenuText.setAddButtonBackground(R.drawable.white_board_text_selected_selector); OperationUtils.getInstance().mCurrentOPerationText = WhiteBoardVariable.Operation.TEXT_CLICK; break; case WhiteBoardVariable.Operation.TEXT_CLICK: int size = OperationUtils.getInstance().getSavePoints().size(); if (size > 0) { DrawPoint dp = OperationUtils.getInstance().getSavePoints().get(size - 1); if (dp.getType() == OperationUtils.DRAW_TEXT && dp.getDrawText().getStatus() == DrawTextView.TEXT_DETAIL) { changeTextBack(); mFabMenuText.expand(); OperationUtils.getInstance().mCurrentOPerationText = WhiteBoardVariable.Operation.TEXT_EXPAND; } } break; case WhiteBoardVariable.Operation.TEXT_EXPAND: mFabMenuText.collapse(); OperationUtils.getInstance().mCurrentOPerationText = WhiteBoardVariable.Operation.TEXT_CLICK; break; } break; case WhiteBoardVariable.Operation.PEN_CLICK: case WhiteBoardVariable.Operation.ERASER_CLICK: switch (OperationUtils.getInstance().mCurrentOPerationText) { case WhiteBoardVariable.Operation.TEXT_NORMAL: break; case WhiteBoardVariable.Operation.TEXT_CLICK: mFabMenuText.clearDraw(); mFabMenuText.setAddButtonBackground(R.drawable.white_board_text_selector); OperationUtils.getInstance().mCurrentOPerationText = WhiteBoardVariable.Operation.TEXT_NORMAL; break; case WhiteBoardVariable.Operation.TEXT_EXPAND: mFabMenuText.collapse(); mFabMenuText.clearDraw(); mFabMenuText.setAddButtonBackground(R.drawable.white_board_text_selector); OperationUtils.getInstance().mCurrentOPerationText = WhiteBoardVariable.Operation.TEXT_NORMAL; break; } break; case WhiteBoardVariable.Operation.COLOR_CLICK: case WhiteBoardVariable.Operation.OUTSIDE_CLICK: switch (OperationUtils.getInstance().mCurrentOPerationText) { case WhiteBoardVariable.Operation.TEXT_NORMAL: break; case WhiteBoardVariable.Operation.TEXT_CLICK: break; case WhiteBoardVariable.Operation.TEXT_EXPAND: mFabMenuText.collapse(); OperationUtils.getInstance().mCurrentOPerationText = WhiteBoardVariable.Operation.TEXT_CLICK; break; } break; } } /** * 白板工具栏点击切换操作-橡皮擦 */ private void setEraserOperation(int currentOperation) { switch (currentOperation) { case WhiteBoardVariable.Operation.ERASER_CLICK: switch (OperationUtils.getInstance().mCurrentOPerationEraser) { case WhiteBoardVariable.Operation.ERASER_NORMAL: OperationUtils.getInstance().mCurrentDrawType = OperationUtils.DRAW_ERASER; mDbView.changeEraser(); mFabMenuEraser.setAddButtonBackground(R.drawable.white_board_eraser_selected_selector); OperationUtils.getInstance().mCurrentOPerationEraser = WhiteBoardVariable.Operation.ERASER_CLICK; break; case WhiteBoardVariable.Operation.ERASER_CLICK: mFabMenuEraser.expand(); changeEraserBack(); OperationUtils.getInstance().mCurrentOPerationEraser = WhiteBoardVariable.Operation.ERASER_EXPAND; break; case WhiteBoardVariable.Operation.ERASER_EXPAND: mFabMenuEraser.collapse(); OperationUtils.getInstance().mCurrentOPerationEraser = WhiteBoardVariable.Operation.ERASER_CLICK; break; } break; case WhiteBoardVariable.Operation.TEXT_CLICK: case WhiteBoardVariable.Operation.PEN_CLICK: switch (OperationUtils.getInstance().mCurrentOPerationEraser) { case WhiteBoardVariable.Operation.ERASER_NORMAL: break; case WhiteBoardVariable.Operation.ERASER_CLICK: mFabMenuEraser.clearDraw(); mFabMenuEraser.setAddButtonBackground(R.drawable.white_board_eraser_selector); OperationUtils.getInstance().mCurrentOPerationEraser = WhiteBoardVariable.Operation.ERASER_NORMAL; break; case WhiteBoardVariable.Operation.ERASER_EXPAND: mFabMenuEraser.collapse(); mFabMenuEraser.clearDraw(); mFabMenuEraser.setAddButtonBackground(R.drawable.white_board_eraser_selector); OperationUtils.getInstance().mCurrentOPerationEraser = WhiteBoardVariable.Operation.ERASER_NORMAL; break; } break; case WhiteBoardVariable.Operation.COLOR_CLICK: case WhiteBoardVariable.Operation.OUTSIDE_CLICK: switch (OperationUtils.getInstance().mCurrentOPerationEraser) { case WhiteBoardVariable.Operation.ERASER_NORMAL: break; case WhiteBoardVariable.Operation.ERASER_CLICK: break; case WhiteBoardVariable.Operation.ERASER_EXPAND: mFabMenuEraser.collapse(); OperationUtils.getInstance().mCurrentOPerationEraser = WhiteBoardVariable.Operation.ERASER_CLICK; break; } break; } } /** * 保存当前白板为图片 */ public void saveImage() { String fileName = StoreUtil.getPhotoSavePath(); Log.e("gpy", fileName); File file = new File(fileName); try { File directory = file.getParentFile(); if (!directory.exists() && !directory.mkdirs()) { showMessage(getString(R.string.white_board_export_fail)); return; } file.createNewFile(); FileOutputStream out = new FileOutputStream(file); mFlView.setDrawingCacheEnabled(true); mFlView.buildDrawingCache(); Bitmap bitmap = mFlView.getDrawingCache(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); out.flush(); out.close(); mFlView.destroyDrawingCache(); Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Uri uri = Uri.fromFile(file); intent.setData(uri); sendBroadcast(intent);//这个广播的目的就是更新图库 showMessage(getString(R.string.white_board_export_tip) + fileName); } catch (Exception e) { showMessage(getString(R.string.white_board_export_fail)); e.printStackTrace(); } } @Override public void onBackPressed() { super.onBackPressed(); } @ReceiveEvents(name = Events.WHITE_BOARD_TEXT_EDIT) private void textEdit() {//文字编辑 mIvWhiteBoardBack.setVisibility(View.GONE); mIvWhiteBoardExport.setVisibility(View.GONE); mIvWhiteBoardSave.setVisibility(View.GONE); mRlBottom.setVisibility(View.GONE); mIvWhiteBoardDisable.setVisibility(View.GONE); // mLayoutParams = (RelativeLayout.LayoutParams) mRlContent.getLayoutParams(); // mLayoutParams.setMargins(OperationUtils.dip2px(24), 0, OperationUtils.dip2px(24), 0); // mRlContent.setLayoutParams(mLayoutParams); mIvWhiteBoardQuit.setVisibility(View.VISIBLE); mIvWhiteBoardConfirm.setVisibility(View.VISIBLE); } @ReceiveEvents(name = Events.WHITE_BOARD_UNDO_REDO) private void showUndoRedo() {//是否显示撤销、重装按钮 if (OperationUtils.getInstance().getSavePoints().isEmpty()) { mIvWhiteBoardUndo.setVisibility(View.INVISIBLE); mIvWhiteBoardExport.setVisibility(View.INVISIBLE); mIvWhiteBoardSave.setVisibility(View.INVISIBLE); } else { mIvWhiteBoardUndo.setVisibility(View.VISIBLE); mIvWhiteBoardExport.setVisibility(View.VISIBLE); mIvWhiteBoardSave.setVisibility(View.VISIBLE); } if (OperationUtils.getInstance().getDeletePoints().isEmpty()) { mIvWhiteBoardRedo.setVisibility(View.INVISIBLE); } else { mIvWhiteBoardRedo.setVisibility(View.VISIBLE); } } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/view/base/BaseActivity.java ================================================ package com.example.gpy.whiteboard.view.base; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; import android.widget.Toast; import com.github.guanpy.library.EventBus; import butterknife.ButterKnife; /** * Created by gpy on 2016/2/17. */ public abstract class BaseActivity extends FragmentActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(this.getLayoutId()); ButterKnife.inject(this); this.afterCreate(savedInstanceState); } protected abstract int getLayoutId(); protected abstract void afterCreate(Bundle savedInstanceState); protected void showMessage(CharSequence msg) { Toast.makeText(this,msg,Toast.LENGTH_SHORT).show(); } @Override protected void onResume() { super.onResume(); EventBus.registerAnnotatedReceiver(this); } @Override protected void onPause() { super.onPause(); EventBus.unregisterAnnotatedReceiver(this); } public void navi2Page(final Class page) { this.navi2Page(page, false); } public void navi2Page(final Class page, final boolean shut) { this.navi2Page(page, null, shut); } public void navi2Page(final Class page, final Bundle data, final boolean shut) { final Intent intent = new Intent(this, page); if (null != data) { intent.putExtras(data); } this.startActivity(intent); if (shut) { this.finish(); } } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/view/widget/floatingactionmenu/AddFloatingActionButton.java ================================================ package com.example.gpy.whiteboard.view.widget.floatingactionmenu; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.Shape; import android.support.annotation.ColorRes; import android.support.annotation.DrawableRes; import android.util.AttributeSet; import com.example.gpy.whiteboard.R; public class AddFloatingActionButton extends FloatingActionButton { int mPlusColor; public AddFloatingActionButton(Context context) { this(context, null); } public AddFloatingActionButton(Context context, AttributeSet attrs) { super(context, attrs); } public AddFloatingActionButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override void init(Context context, AttributeSet attributeSet) { TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.AddFloatingActionButton, 0, 0); mPlusColor = attr.getColor(R.styleable.AddFloatingActionButton_fab_plusIconColor, getColor(android.R.color.white)); attr.recycle(); super.init(context, attributeSet); } /** * @return the current Color of plus icon. */ public int getPlusColor() { return mPlusColor; } public void setPlusColorResId(@ColorRes int plusColor) { setPlusColor(getColor(plusColor)); } public void setPlusColor(int color) { if (mPlusColor != color) { mPlusColor = color; updateBackground(); } } @Override public void setIcon(@DrawableRes int icon) { throw new UnsupportedOperationException("Use FloatingActionButton if you want to use custom icon"); } @Override Drawable getIconDrawable() { final float iconSize = getDimension(R.dimen.fab_icon_size); final float iconHalfSize = iconSize / 2f; final float plusSize = getDimension(R.dimen.fab_plus_icon_size); final float plusHalfStroke = getDimension(R.dimen.fab_plus_icon_stroke) / 2f; final float plusOffset = (iconSize - plusSize) / 2f; final Shape shape = new Shape() { @Override public void draw(Canvas canvas, Paint paint) { canvas.drawRect(plusOffset, iconHalfSize - plusHalfStroke, iconSize - plusOffset, iconHalfSize + plusHalfStroke, paint); canvas.drawRect(iconHalfSize - plusHalfStroke, plusOffset, iconHalfSize + plusHalfStroke, iconSize - plusOffset, paint); } }; ShapeDrawable drawable = new ShapeDrawable(shape); final Paint paint = drawable.getPaint(); paint.setColor(mPlusColor); paint.setStyle(Style.FILL); paint.setAntiAlias(true); return drawable; } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/view/widget/floatingactionmenu/FloatingActionButton.java ================================================ package com.example.gpy.whiteboard.view.widget.floatingactionmenu; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.Shader.TileMode; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.ShapeDrawable.ShaderFactory; import android.graphics.drawable.StateListDrawable; import android.graphics.drawable.shapes.OvalShape; import android.os.Build; import android.os.Build.VERSION_CODES; import android.support.annotation.ColorRes; import android.support.annotation.DimenRes; import android.support.annotation.DrawableRes; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.widget.TextView; import com.example.gpy.whiteboard.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public class FloatingActionButton extends FloatingImageButton { public static final int SIZE_NORMAL = 0; public static final int SIZE_MINI = 1; @Retention(RetentionPolicy.SOURCE) @IntDef({ SIZE_NORMAL, SIZE_MINI }) public @interface FAB_SIZE { } int mColorNormal; int mColorPressed; int mColorDisabled; String mTitle; @DrawableRes private int mIcon; private Drawable mIconDrawable; private int mSize; private float mCircleSize; private float mShadowRadius; private float mShadowOffset; private int mDrawableSize; boolean mStrokeVisible; public FloatingActionButton(Context context) { this(context, null); } public FloatingActionButton(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public FloatingActionButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } void init(Context context, AttributeSet attributeSet) { TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionButton, 0, 0); mColorNormal = attr.getColor(R.styleable.FloatingActionButton_fab_colorNormal, getColor(android.R.color.holo_blue_dark)); mColorPressed = attr.getColor(R.styleable.FloatingActionButton_fab_colorPressed, getColor(android.R.color.holo_blue_light)); mColorDisabled = attr.getColor(R.styleable.FloatingActionButton_fab_colorDisabled, getColor(android.R.color.darker_gray)); mSize = attr.getInt(R.styleable.FloatingActionButton_fab_size, SIZE_NORMAL); mIcon = attr.getResourceId(R.styleable.FloatingActionButton_fab_icon, 0); mTitle = attr.getString(R.styleable.FloatingActionButton_fab_title); mStrokeVisible = attr.getBoolean(R.styleable.FloatingActionButton_fab_stroke_visible, true); attr.recycle(); updateCircleSize(); mShadowRadius = getDimension(R.dimen.fab_shadow_radius); mShadowOffset = getDimension(R.dimen.fab_shadow_offset); updateDrawableSize(); updateBackground(); } private void updateDrawableSize() { mDrawableSize = (int) (mCircleSize + 2 * mShadowRadius); } private void updateCircleSize() { mCircleSize = getDimension(mSize == SIZE_NORMAL ? R.dimen.fab_size_normal : R.dimen.fab_size_mini); } public void setSize(@FAB_SIZE int size) { if (size != SIZE_MINI && size != SIZE_NORMAL) { throw new IllegalArgumentException("Use @FAB_SIZE constants only!"); } if (mSize != size) { mSize = size; updateCircleSize(); updateDrawableSize(); updateBackground(); } } @FAB_SIZE public int getSize() { return mSize; } public void setIcon(@DrawableRes int icon) { if (mIcon != icon) { mIcon = icon; mIconDrawable = null; updateBackground(); } } public void setIconDrawable(@NonNull Drawable iconDrawable) { if (mIconDrawable != iconDrawable) { mIcon = 0; mIconDrawable = iconDrawable; updateBackground(); } } /** * @return the current Color for normal state. */ public int getColorNormal() { return mColorNormal; } public void setColorNormalResId(@ColorRes int colorNormal) { setColorNormal(getColor(colorNormal)); } public void setColorNormal(int color) { if (mColorNormal != color) { mColorNormal = color; updateBackground(); } } /** * @return the current color for pressed state. */ public int getColorPressed() { return mColorPressed; } public void setColorPressedResId(@ColorRes int colorPressed) { setColorPressed(getColor(colorPressed)); } public void setColorPressed(int color) { if (mColorPressed != color) { mColorPressed = color; updateBackground(); } } /** * @return the current color for disabled state. */ public int getColorDisabled() { return mColorDisabled; } public void setColorDisabledResId(@ColorRes int colorDisabled) { setColorDisabled(getColor(colorDisabled)); } public void setColorDisabled(int color) { if (mColorDisabled != color) { mColorDisabled = color; updateBackground(); } } public void setStrokeVisible(boolean visible) { if (mStrokeVisible != visible) { mStrokeVisible = visible; updateBackground(); } } public boolean isStrokeVisible() { return mStrokeVisible; } int getColor(@ColorRes int id) { return getResources().getColor(id); } float getDimension(@DimenRes int id) { return getResources().getDimension(id); } public void setTitle(String title) { mTitle = title; TextView label = getLabelView(); if (label != null) { label.setText(title); } } TextView getLabelView() { return (TextView) getTag(R.id.fab_label); } public String getTitle() { return mTitle; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(mDrawableSize, mDrawableSize); } void updateBackground() { final float strokeWidth = getDimension(R.dimen.fab_stroke_width); final float halfStrokeWidth = strokeWidth / 2f; LayerDrawable layerDrawable = new LayerDrawable( new Drawable[] { getResources().getDrawable(mSize == SIZE_NORMAL ? R.drawable.fab_bg_normal : R.drawable.fab_bg_mini), createFillDrawable(strokeWidth), createOuterStrokeDrawable(strokeWidth), getIconDrawable() }); int iconOffset = (int) (mCircleSize - getDimension(R.dimen.fab_icon_size)) / 2; int circleInsetHorizontal = (int) (mShadowRadius); int circleInsetTop = (int) (mShadowRadius - mShadowOffset); int circleInsetBottom = (int) (mShadowRadius + mShadowOffset); layerDrawable.setLayerInset(1, circleInsetHorizontal, circleInsetTop, circleInsetHorizontal, circleInsetBottom); layerDrawable.setLayerInset(2, (int) (circleInsetHorizontal - halfStrokeWidth), (int) (circleInsetTop - halfStrokeWidth), (int) (circleInsetHorizontal - halfStrokeWidth), (int) (circleInsetBottom - halfStrokeWidth)); layerDrawable.setLayerInset(3, circleInsetHorizontal + iconOffset, circleInsetTop + iconOffset, circleInsetHorizontal + iconOffset, circleInsetBottom + iconOffset); setBackgroundCompat(layerDrawable); } Drawable getIconDrawable() { if (mIconDrawable != null) { return mIconDrawable; } else if (mIcon != 0) { return getResources().getDrawable(mIcon); } else { return new ColorDrawable(Color.TRANSPARENT); } } private StateListDrawable createFillDrawable(float strokeWidth) { StateListDrawable drawable = new StateListDrawable(); drawable.addState(new int[] { -android.R.attr.state_enabled }, createCircleDrawable(mColorDisabled, strokeWidth)); drawable.addState(new int[] { android.R.attr.state_pressed }, createCircleDrawable(mColorPressed, strokeWidth)); drawable.addState(new int[] { }, createCircleDrawable(mColorNormal, strokeWidth)); return drawable; } private Drawable createCircleDrawable(int color, float strokeWidth) { int alpha = Color.alpha(color); int opaqueColor = opaque(color); ShapeDrawable fillDrawable = new ShapeDrawable(new OvalShape()); final Paint paint = fillDrawable.getPaint(); paint.setAntiAlias(true); paint.setColor(opaqueColor); Drawable[] layers = { fillDrawable, createInnerStrokesDrawable(opaqueColor, strokeWidth) }; LayerDrawable drawable = alpha == 255 || !mStrokeVisible ? new LayerDrawable(layers) : new TranslucentLayerDrawable(alpha, layers); int halfStrokeWidth = (int) (strokeWidth / 2f); drawable.setLayerInset(1, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth); return drawable; } private static class TranslucentLayerDrawable extends LayerDrawable { private final int mAlpha; public TranslucentLayerDrawable(int alpha, Drawable... layers) { super(layers); mAlpha = alpha; } @Override public void draw(Canvas canvas) { Rect bounds = getBounds(); canvas.saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom, mAlpha, Canvas.ALL_SAVE_FLAG); super.draw(canvas); canvas.restore(); } } private Drawable createOuterStrokeDrawable(float strokeWidth) { ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape()); final Paint paint = shapeDrawable.getPaint(); paint.setAntiAlias(true); paint.setStrokeWidth(strokeWidth); paint.setStyle(Style.STROKE); paint.setColor(Color.BLACK); paint.setAlpha(opacityToAlpha(0.02f)); return shapeDrawable; } private int opacityToAlpha(float opacity) { return (int) (255f * opacity); } private int darkenColor(int argb) { return adjustColorBrightness(argb, 0.9f); } private int lightenColor(int argb) { return adjustColorBrightness(argb, 1.1f); } private int adjustColorBrightness(int argb, float factor) { float[] hsv = new float[3]; Color.colorToHSV(argb, hsv); hsv[2] = Math.min(hsv[2] * factor, 1f); return Color.HSVToColor(Color.alpha(argb), hsv); } private int halfTransparent(int argb) { return Color.argb( Color.alpha(argb) / 2, Color.red(argb), Color.green(argb), Color.blue(argb) ); } private int opaque(int argb) { return Color.rgb( Color.red(argb), Color.green(argb), Color.blue(argb) ); } private Drawable createInnerStrokesDrawable(final int color, float strokeWidth) { if (!mStrokeVisible) { return new ColorDrawable(Color.TRANSPARENT); } ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape()); final int bottomStrokeColor = darkenColor(color); final int bottomStrokeColorHalfTransparent = halfTransparent(bottomStrokeColor); final int topStrokeColor = lightenColor(color); final int topStrokeColorHalfTransparent = halfTransparent(topStrokeColor); final Paint paint = shapeDrawable.getPaint(); paint.setAntiAlias(true); paint.setStrokeWidth(strokeWidth); paint.setStyle(Style.STROKE); shapeDrawable.setShaderFactory(new ShaderFactory() { @Override public Shader resize(int width, int height) { return new LinearGradient(width / 2, 0, width / 2, height, new int[] { topStrokeColor, topStrokeColorHalfTransparent, color, bottomStrokeColorHalfTransparent, bottomStrokeColor }, new float[] { 0f, 0.2f, 0.5f, 0.8f, 1f }, TileMode.CLAMP ); } }); return shapeDrawable; } @SuppressWarnings("deprecation") @SuppressLint("NewApi") private void setBackgroundCompat(Drawable drawable) { if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { setBackground(drawable); } else { setBackgroundDrawable(drawable); } } @Override public void setVisibility(int visibility) { TextView label = getLabelView(); if (label != null) { label.setVisibility(visibility); } super.setVisibility(visibility); } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/view/widget/floatingactionmenu/FloatingActionsMenu.java ================================================ package com.example.gpy.whiteboard.view.widget.floatingactionmenu; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.ColorRes; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.view.TouchDelegate; import android.view.View; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; import android.widget.ImageView; import android.widget.TextView; import com.example.gpy.whiteboard.R; import com.github.guanpy.wblib.utils.AppContextUtil; import com.github.guanpy.wblib.utils.WhiteBoardVariable; public class FloatingActionsMenu extends ViewGroup { public static final int STYLE_DEFAULT = 0; public static final int STYLE_CUSTOM = 1; public static final int EXPAND_UP = 0; public static final int EXPAND_DOWN = 1; public static final int EXPAND_LEFT = 2; public static final int EXPAND_RIGHT = 3; public static final int LABELS_ON_LEFT_SIDE = 0; public static final int LABELS_ON_RIGHT_SIDE = 1; private static final int ANIMATION_DURATION = 300; private static final float COLLAPSED_PLUS_ROTATION = 0f; private static final float EXPANDED_PLUS_ROTATION = 90f + 45f; private Drawable mAddButtonBackground; private float mAddButtonWidth; private float mAddButtonHeight; private int mAddButtonStyle; private int mAddButtonPlusColor; private int mAddButtonColorNormal; private int mAddButtonColorPressed; private int mAddButtonSize; private boolean mAddButtonStrokeVisible; private int mExpandDirection; private int mButtonSpacing; private int mLabelsMargin; private int mLabelsVerticalOffset; private boolean mExpanded; private AnimatorSet mExpandAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION); private AnimatorSet mCollapseAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION); // private AddFloatingActionButton mAddButton; private FloatingImageButton mAddButton; private RotatingDrawable mRotatingDrawable; private int mMaxButtonWidth; private int mMaxButtonHeight; private int mLabelsStyle; private int mLabelsPosition; private int mButtonsCount; private TouchDelegateGroup mTouchDelegateGroup; private OnFloatingActionsMenuUpdateListener mListener; private OnFloatingActionsMenuClickListener mClickListener; public interface OnFloatingActionsMenuUpdateListener { void onMenuExpanded(); void onMenuCollapsed(); } public FloatingActionsMenu(Context context) { this(context, null); } public FloatingActionsMenu(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public FloatingActionsMenu(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } public interface OnFloatingActionsMenuClickListener{ void addButtonLister(); } public void setOnFloatingActionsMenuClickListener(OnFloatingActionsMenuClickListener flLister){ mClickListener = flLister; } /** * 在图片中间画圆环 * @param size 圆的半径 * @param color 圆的颜色 * */ /**控制按钮外圈*/ public void drawRing(int color){ mAddButton.drawRing(WhiteBoardVariable.RingSize.LARRGE, color); } /** * 清除绘画 * */ public void clearDraw(){ mAddButton.clearDraw(); } /** * 切换控制按钮背景图 * */ public void setAddButtonBackground(int drawable){ mAddButton.setBackgroundResource(drawable); } /** * dip转px */ private int dip2px(float dpValue) { final float scale = AppContextUtil.getContext().getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } private void init(Context context, AttributeSet attributeSet) { // mButtonSpacing = (int) (getResources().getDimension(R.dimen.fab_actions_spacing) - getResources().getDimension(R.dimen.fab_shadow_radius) - getResources().getDimension(R.dimen.fab_shadow_offset)); mLabelsVerticalOffset = getResources().getDimensionPixelSize(R.dimen.fab_shadow_offset); mTouchDelegateGroup = new TouchDelegateGroup(this); setTouchDelegate(mTouchDelegateGroup); TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionsMenu, 0, 0); mButtonSpacing =dip2px(attr.getDimension(R.styleable.FloatingActionsMenu_fab_buttonSpacing, 4)); mAddButtonBackground = attr.getDrawable(R.styleable.FloatingActionsMenu_fab_addButtonBackground); mAddButtonWidth = attr.getDimension(R.styleable.FloatingActionsMenu_fab_addButtonWidth, 0); mAddButtonHeight = attr.getDimension(R.styleable.FloatingActionsMenu_fab_addButtonHeight, 0); mAddButtonStyle = attr.getInt(R.styleable.FloatingActionsMenu_fab_addButtonStyle, STYLE_DEFAULT); mAddButtonPlusColor = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonPlusIconColor, getColor(android.R.color.white)); mAddButtonColorNormal = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorNormal, getColor(android.R.color.holo_blue_dark)); mAddButtonColorPressed = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorPressed, getColor(android.R.color.holo_blue_light)); mAddButtonSize = attr.getInt(R.styleable.FloatingActionsMenu_fab_addButtonSize, FloatingActionButton.SIZE_NORMAL); mAddButtonStrokeVisible = attr.getBoolean(R.styleable.FloatingActionsMenu_fab_addButtonStrokeVisible, true); mExpandDirection = attr.getInt(R.styleable.FloatingActionsMenu_fab_expandDirection, EXPAND_UP); mLabelsStyle = attr.getResourceId(R.styleable.FloatingActionsMenu_fab_labelStyle, 0); mLabelsPosition = attr.getInt(R.styleable.FloatingActionsMenu_fab_labelsPosition, LABELS_ON_LEFT_SIDE); attr.recycle(); if (mLabelsStyle != 0 && expandsHorizontally()) { throw new IllegalStateException("Action labels in horizontal expand orientation is not supported."); } createAddButton(context); } public void setOnFloatingActionsMenuUpdateListener(OnFloatingActionsMenuUpdateListener listener) { mListener = listener; } private boolean expandsHorizontally() { return mExpandDirection == EXPAND_LEFT || mExpandDirection == EXPAND_RIGHT; } private static class RotatingDrawable extends LayerDrawable { public RotatingDrawable(Drawable drawable) { super(new Drawable[]{drawable}); } private float mRotation; @SuppressWarnings("UnusedDeclaration") public float getRotation() { return mRotation; } @SuppressWarnings("UnusedDeclaration") public void setRotation(float rotation) { mRotation = rotation; invalidateSelf(); } @Override public void draw(Canvas canvas) { canvas.save(); canvas.rotate(mRotation, getBounds().centerX(), getBounds().centerY()); super.draw(canvas); canvas.restore(); } } /** * 增加菜单的控制按钮 */ private void createAddButton(Context context) { if (mAddButtonStyle == STYLE_DEFAULT) { mAddButton = new AddFloatingActionButton(context) { @Override void updateBackground() { mPlusColor = mAddButtonPlusColor; mColorNormal = mAddButtonColorNormal; mColorPressed = mAddButtonColorPressed; mStrokeVisible = mAddButtonStrokeVisible; super.updateBackground(); } @Override Drawable getIconDrawable() { final RotatingDrawable rotatingDrawable = new RotatingDrawable(super.getIconDrawable()); mRotatingDrawable = rotatingDrawable; final OvershootInterpolator interpolator = new OvershootInterpolator(); final ObjectAnimator collapseAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", EXPANDED_PLUS_ROTATION, COLLAPSED_PLUS_ROTATION); final ObjectAnimator expandAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", COLLAPSED_PLUS_ROTATION, EXPANDED_PLUS_ROTATION); collapseAnimator.setInterpolator(interpolator); expandAnimator.setInterpolator(interpolator); mExpandAnimation.play(expandAnimator); mCollapseAnimation.play(collapseAnimator); return rotatingDrawable; } }; mAddButton.setId(R.id.fab_expand_menu_button); ((AddFloatingActionButton) mAddButton).setSize(mAddButtonSize); } else if (mAddButtonStyle == STYLE_CUSTOM) { mAddButton = new FloatingImageButton(context); mAddButton.setId(R.id.fab_expand_menu_button); ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(dip2px(mAddButtonWidth), dip2px(mAddButtonHeight)); mAddButton.setLayoutParams(layoutParams); mAddButton.setBackgroundDrawable(mAddButtonBackground); mAddButton.setScaleType(ImageView.ScaleType.CENTER_CROP); } mAddButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(null!=mClickListener){ mClickListener.addButtonLister(); }else{ toggle(); } } }); addView(mAddButton, super.generateDefaultLayoutParams()); } private int getColor(@ColorRes int id) { return getResources().getColor(id); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildren(widthMeasureSpec, heightMeasureSpec); int width = 0; int height = 0; mMaxButtonWidth = 0; mMaxButtonHeight = 0; int maxLabelWidth = 0; for (int i = 0; i < mButtonsCount; i++) { View child = getChildAt(i); if (child.getVisibility() == GONE) { continue; } switch (mExpandDirection) { case EXPAND_UP: case EXPAND_DOWN: mMaxButtonWidth = Math.max(mMaxButtonWidth, child.getMeasuredWidth()); height += child.getMeasuredHeight(); break; case EXPAND_LEFT: case EXPAND_RIGHT: width += child.getMeasuredWidth(); mMaxButtonHeight = Math.max(mMaxButtonHeight, child.getMeasuredHeight()); break; } if (!expandsHorizontally()) { TextView label = (TextView) child.getTag(R.id.fab_label); if (label != null) { maxLabelWidth = Math.max(maxLabelWidth, label.getMeasuredWidth()); } } } if (!expandsHorizontally()) { width = mMaxButtonWidth + (maxLabelWidth > 0 ? maxLabelWidth + mLabelsMargin : 0); } else { height = mMaxButtonHeight; } switch (mExpandDirection) { case EXPAND_UP: case EXPAND_DOWN: height += mButtonSpacing * (getChildCount() - 1); height = adjustForOvershoot(height); break; case EXPAND_LEFT: case EXPAND_RIGHT: width += mButtonSpacing * (getChildCount() - 1); width = adjustForOvershoot(width); break; } setMeasuredDimension(width, height); } private int adjustForOvershoot(int dimension) { return dimension * 12 / 10; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { switch (mExpandDirection) { case EXPAND_UP: case EXPAND_DOWN: boolean expandUp = mExpandDirection == EXPAND_UP; if (changed) { mTouchDelegateGroup.clearTouchDelegates(); } int addButtonY = expandUp ? b - t - mAddButton.getMeasuredHeight() : 0; // Ensure mAddButton is centered on the line where the buttons should be int buttonsHorizontalCenter = mLabelsPosition == LABELS_ON_LEFT_SIDE ? r - l - mMaxButtonWidth / 2 : mMaxButtonWidth / 2; int addButtonLeft = buttonsHorizontalCenter - mAddButton.getMeasuredWidth() / 2; mAddButton.layout(addButtonLeft, addButtonY, addButtonLeft + mAddButton.getMeasuredWidth(), addButtonY + mAddButton.getMeasuredHeight()); int labelsOffset = mMaxButtonWidth / 2 + mLabelsMargin; int labelsXNearButton = mLabelsPosition == LABELS_ON_LEFT_SIDE ? buttonsHorizontalCenter - labelsOffset : buttonsHorizontalCenter + labelsOffset; int nextY = expandUp ? addButtonY - mButtonSpacing : addButtonY + mAddButton.getMeasuredHeight() + mButtonSpacing; for (int i = mButtonsCount - 1; i >= 0; i--) { final View child = getChildAt(i); if (child == mAddButton || child.getVisibility() == GONE) continue; int childX = buttonsHorizontalCenter - child.getMeasuredWidth() / 2; int childY = expandUp ? nextY - child.getMeasuredHeight() : nextY; child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight()); float collapsedTranslation = addButtonY - childY; float expandedTranslation = 0f; child.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation); child.setAlpha(mExpanded ? 1f : 0f); LayoutParams params = (LayoutParams) child.getLayoutParams(); params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation); params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation); params.setAnimationsTarget(child); View label = (View) child.getTag(R.id.fab_label); if (label != null) { int labelXAwayFromButton = mLabelsPosition == LABELS_ON_LEFT_SIDE ? labelsXNearButton - label.getMeasuredWidth() : labelsXNearButton + label.getMeasuredWidth(); int labelLeft = mLabelsPosition == LABELS_ON_LEFT_SIDE ? labelXAwayFromButton : labelsXNearButton; int labelRight = mLabelsPosition == LABELS_ON_LEFT_SIDE ? labelsXNearButton : labelXAwayFromButton; int labelTop = childY - mLabelsVerticalOffset + (child.getMeasuredHeight() - label.getMeasuredHeight()) / 2; label.layout(labelLeft, labelTop, labelRight, labelTop + label.getMeasuredHeight()); Rect touchArea = new Rect( Math.min(childX, labelLeft), childY - mButtonSpacing / 2, Math.max(childX + child.getMeasuredWidth(), labelRight), childY + child.getMeasuredHeight() + mButtonSpacing / 2); mTouchDelegateGroup.addTouchDelegate(new TouchDelegate(touchArea, child)); label.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation); label.setAlpha(mExpanded ? 1f : 0f); LayoutParams labelParams = (LayoutParams) label.getLayoutParams(); labelParams.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation); labelParams.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation); labelParams.setAnimationsTarget(label); } nextY = expandUp ? childY - mButtonSpacing : childY + child.getMeasuredHeight() + mButtonSpacing; } break; case EXPAND_LEFT: case EXPAND_RIGHT: boolean expandLeft = mExpandDirection == EXPAND_LEFT; int addButtonX = expandLeft ? r - l - mAddButton.getMeasuredWidth() : 0; // Ensure mAddButton is centered on the line where the buttons should be int addButtonTop = b - t - mMaxButtonHeight + (mMaxButtonHeight - mAddButton.getMeasuredHeight()) / 2; mAddButton.layout(addButtonX, addButtonTop, addButtonX + mAddButton.getMeasuredWidth(), addButtonTop + mAddButton.getMeasuredHeight()); int nextX = expandLeft ? addButtonX - mButtonSpacing : addButtonX + mAddButton.getMeasuredWidth() + mButtonSpacing; for (int i = mButtonsCount - 1; i >= 0; i--) { final View child = getChildAt(i); if (child == mAddButton || child.getVisibility() == GONE) continue; int childX = expandLeft ? nextX - child.getMeasuredWidth() : nextX; int childY = addButtonTop + (mAddButton.getMeasuredHeight() - child.getMeasuredHeight()) / 2; child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight()); float collapsedTranslation = addButtonX - childX; float expandedTranslation = 0f; child.setTranslationX(mExpanded ? expandedTranslation : collapsedTranslation); child.setAlpha(mExpanded ? 1f : 0f); LayoutParams params = (LayoutParams) child.getLayoutParams(); params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation); params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation); params.setAnimationsTarget(child); nextX = expandLeft ? childX - mButtonSpacing : childX + child.getMeasuredWidth() + mButtonSpacing; } break; } } @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(super.generateDefaultLayoutParams()); } @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(super.generateLayoutParams(attrs)); } @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new LayoutParams(super.generateLayoutParams(p)); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return super.checkLayoutParams(p); } private static Interpolator sExpandInterpolator = new OvershootInterpolator(); private static Interpolator sCollapseInterpolator = new DecelerateInterpolator(3f); private static Interpolator sAlphaExpandInterpolator = new DecelerateInterpolator(); private class LayoutParams extends ViewGroup.LayoutParams { private ObjectAnimator mExpandDir = new ObjectAnimator(); private ObjectAnimator mExpandAlpha = new ObjectAnimator(); private ObjectAnimator mCollapseDir = new ObjectAnimator(); private ObjectAnimator mCollapseAlpha = new ObjectAnimator(); private boolean animationsSetToPlay; public LayoutParams(ViewGroup.LayoutParams source) { super(source); mExpandDir.setInterpolator(sExpandInterpolator); mExpandAlpha.setInterpolator(sAlphaExpandInterpolator); mCollapseDir.setInterpolator(sCollapseInterpolator); mCollapseAlpha.setInterpolator(sCollapseInterpolator); mCollapseAlpha.setProperty(View.ALPHA); mCollapseAlpha.setFloatValues(1f, 0f); mExpandAlpha.setProperty(View.ALPHA); mExpandAlpha.setFloatValues(0f, 1f); switch (mExpandDirection) { case EXPAND_UP: case EXPAND_DOWN: mCollapseDir.setProperty(View.TRANSLATION_Y); mExpandDir.setProperty(View.TRANSLATION_Y); break; case EXPAND_LEFT: case EXPAND_RIGHT: mCollapseDir.setProperty(View.TRANSLATION_X); mExpandDir.setProperty(View.TRANSLATION_X); break; } } public void setAnimationsTarget(View view) { mCollapseAlpha.setTarget(view); mCollapseDir.setTarget(view); mExpandAlpha.setTarget(view); mExpandDir.setTarget(view); // Now that the animations have targets, set them to be played if (!animationsSetToPlay) { mCollapseAnimation.play(mCollapseAlpha); mCollapseAnimation.play(mCollapseDir); mExpandAnimation.play(mExpandAlpha); mExpandAnimation.play(mExpandDir); animationsSetToPlay = true; } } } @Override protected void onFinishInflate() { super.onFinishInflate(); bringChildToFront(mAddButton); mButtonsCount = getChildCount(); } public void collapse() { if (mExpanded) { mExpanded = false; mTouchDelegateGroup.setEnabled(false); mCollapseAnimation.start(); mExpandAnimation.cancel(); if (mListener != null) { mListener.onMenuCollapsed(); } } } public void toggle() { if (mExpanded) { collapse(); } else { expand(); } } public void expand() { if (!mExpanded) { mExpanded = true; mTouchDelegateGroup.setEnabled(true); mCollapseAnimation.cancel(); mExpandAnimation.start(); if (mListener != null) { mListener.onMenuExpanded(); } } } public boolean isExpanded() { return mExpanded; } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.mExpanded = mExpanded; return savedState; } @Override public void onRestoreInstanceState(Parcelable state) { if (state instanceof SavedState) { SavedState savedState = (SavedState) state; mExpanded = savedState.mExpanded; mTouchDelegateGroup.setEnabled(mExpanded); if (mRotatingDrawable != null) { mRotatingDrawable.setRotation(mExpanded ? EXPANDED_PLUS_ROTATION : COLLAPSED_PLUS_ROTATION); } super.onRestoreInstanceState(savedState.getSuperState()); } else { super.onRestoreInstanceState(state); } } public static class SavedState extends BaseSavedState { public boolean mExpanded; public SavedState(Parcelable parcel) { super(parcel); } private SavedState(Parcel in) { super(in); mExpanded = in.readInt() == 1; } @Override public void writeToParcel(@NonNull Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(mExpanded ? 1 : 0); } public static final Creator CREATOR = new Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/view/widget/floatingactionmenu/FloatingImageButton.java ================================================ package com.example.gpy.whiteboard.view.widget.floatingactionmenu; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.widget.ImageButton; import com.github.guanpy.wblib.utils.OperationUtils; import com.github.guanpy.wblib.utils.WhiteBoardVariable; /** * FloatingActionButton 不具备的功能 */ public class FloatingImageButton extends ImageButton { Paint mPaint; /**不做绘画操作*/ static final int DRAW_NULL = 0; /**画圆环*/ static final int DRAW_RING = 1; /**画圆*/ static final int DRAW_CIRCLE= 2; /**画圆和圆环*/ static final int DRAW_CIRCLE_AND_RING= 3; int mDrawType ; public FloatingImageButton(Context context) { this(context, null); } public FloatingImageButton(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public FloatingImageButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context){ mPaint = new Paint(); mPaint.setAntiAlias(true); //消除锯齿 } /** * 在图片中间画圆环(按钮小圆环) * @param color 圆的颜色 * */ public void drawMiniRing(int color){ drawRing(WhiteBoardVariable.RingSize.MINI, color); } /** * 在图片中间画圆环(按钮大圆环) * @param color 圆的颜色 * */ public void drawLargeRing(int color){ drawRing(WhiteBoardVariable.RingSize.LARRGE, color); } /** * 在图片中间画圆环 * @param size 圆的半径 * @param color 圆的颜色 * */ public void drawRing(int size,int color){ mPaint.setColor(color); mPaint.setStrokeWidth(size); mPaint.setStyle(Paint.Style.STROKE); //绘制空心圆 mDrawType = DRAW_RING; postInvalidate(); } /** * 在图片中间画圆 * @param size 圆的半径 * */ public void drawCircle(int size){ drawCircle(size, OperationUtils.getInstance().mCurrentColor); } /** * 在图片中间画圆 * @param size 圆的半径 * @param color 圆的颜色 * */ public void drawCircle(int size,int color){ mPaint.setColor(color); mPaint.setStrokeWidth(size); mPaint.setStyle(Paint.Style.FILL); //绘制实心圆 mDrawType = DRAW_CIRCLE; postInvalidate(); } /** * 在图片中间画圆和外圈圆环 * @param size 圆的半径 * @param color 圆的颜色 * */ public void drawCircleAndRing(int size,int color){ mPaint.setColor(color); mPaint.setStrokeWidth(size); mPaint.setStyle(Paint.Style.FILL); //绘制实心圆 mDrawType = DRAW_CIRCLE_AND_RING; postInvalidate(); } /** * 清除绘画 * */ public void clearDraw(){ mDrawType = DRAW_NULL; postInvalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int center = getWidth()/2; if(mDrawType==DRAW_RING){ canvas.drawCircle(center, center, center - mPaint.getStrokeWidth(),mPaint); }else if(mDrawType==DRAW_CIRCLE){ canvas.drawCircle(center, center, mPaint.getStrokeWidth(),mPaint); }else if(mDrawType==DRAW_CIRCLE_AND_RING){ canvas.drawCircle(center, center, mPaint.getStrokeWidth(), mPaint); mPaint.setStrokeWidth(WhiteBoardVariable.RingSize.MINI); mPaint.setStyle(Paint.Style.STROKE); //绘制空心圆 canvas.drawCircle(center, center, center - WhiteBoardVariable.RingSize.MINI, mPaint); } } } ================================================ FILE: app/src/main/java/com/example/gpy/whiteboard/view/widget/floatingactionmenu/TouchDelegateGroup.java ================================================ package com.example.gpy.whiteboard.view.widget.floatingactionmenu; import android.graphics.Rect; import android.support.annotation.NonNull; import android.view.MotionEvent; import android.view.TouchDelegate; import android.view.View; import java.util.ArrayList; public class TouchDelegateGroup extends TouchDelegate { private static final Rect USELESS_HACKY_RECT = new Rect(); private final ArrayList mTouchDelegates = new ArrayList(); private TouchDelegate mCurrentTouchDelegate; private boolean mEnabled; public TouchDelegateGroup(View uselessHackyView) { super(USELESS_HACKY_RECT, uselessHackyView); } public void addTouchDelegate(@NonNull TouchDelegate touchDelegate) { mTouchDelegates.add(touchDelegate); } public void removeTouchDelegate(TouchDelegate touchDelegate) { mTouchDelegates.remove(touchDelegate); if (mCurrentTouchDelegate == touchDelegate) { mCurrentTouchDelegate = null; } } public void clearTouchDelegates() { mTouchDelegates.clear(); mCurrentTouchDelegate = null; } @Override public boolean onTouchEvent(@NonNull MotionEvent event) { if (!mEnabled) return false; TouchDelegate delegate = null; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: for (int i = 0; i < mTouchDelegates.size(); i++) { TouchDelegate touchDelegate = mTouchDelegates.get(i); if (touchDelegate.onTouchEvent(event)) { mCurrentTouchDelegate = touchDelegate; return true; } } break; case MotionEvent.ACTION_MOVE: delegate = mCurrentTouchDelegate; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: delegate = mCurrentTouchDelegate; mCurrentTouchDelegate = null; break; } return delegate != null && delegate.onTouchEvent(event); } public void setEnabled(boolean enabled) { mEnabled = enabled; } } ================================================ FILE: app/src/main/res/drawable/bg_white_board.xml ================================================ ================================================ FILE: app/src/main/res/drawable/common_cancel_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/common_confirm_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/draw_text_border.xml ================================================ ================================================ FILE: app/src/main/res/drawable/fab_label_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_back_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_add_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_color_black_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_color_green_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_color_orange_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_color_pink_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_color_purple_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_disable_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_eraser_selected_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_eraser_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_export_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_next_page_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_pen_selected_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_pen_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_pre_page_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_redo_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_save_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_text_bold_selected_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_text_bold_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_text_italics_selected_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_text_italics_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_text_selected_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_text_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_text_underline_selected_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_text_underline_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_undisable_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white_board_undo_selector.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_white_board.xml ================================================ ================================================ FILE: app/src/main/res/layout/draw_text.xml ================================================