├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── lee │ │ └── drawlayoutsample │ │ └── ExampleInstrumentedTest.java │ ├── assets │ ├── View coordinates.png │ ├── all_black.svg │ ├── all_green.svg │ ├── all_red.svg │ ├── hot_black.svg │ ├── hot_green.svg │ ├── hot_red.svg │ ├── jewelry_black.svg │ ├── jewelry_green.svg │ ├── jewelry_red.svg │ ├── line_black.svg │ ├── line_green.svg │ ├── line_red.svg │ ├── smile_black.svg │ ├── smile_green.svg │ └── smile_red.svg │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── lee │ │ │ └── drawlayoutsample │ │ │ ├── App.java │ │ │ ├── EditTextInfo.java │ │ │ ├── ElementMemento.java │ │ │ ├── IElementMemento.java │ │ │ ├── MainActivity.java │ │ │ ├── MementoCreataker.java │ │ │ ├── MementoOriginator.java │ │ │ ├── ViewInfo.java │ │ │ └── utils │ │ │ ├── BaseUtils.java │ │ │ └── LogUtils.java │ └── res │ │ ├── drawable-hdpi │ │ ├── actionbar_ic_cancel.png │ │ ├── actionbar_ic_copy.png │ │ ├── actionbar_ic_delete.png │ │ ├── actionbar_ic_done.png │ │ ├── all_normal.png │ │ ├── all_selected.png │ │ ├── back.png │ │ ├── forward.png │ │ ├── gallery_toolbar_chinabrush_press.png │ │ ├── hot_normal.png │ │ ├── hot_selected.png │ │ ├── ic_menu_rotate_light_n.png │ │ ├── jewelry_normal.png │ │ ├── jewelry_selected.png │ │ ├── line.png │ │ ├── line_nomal.png │ │ ├── line_selected.png │ │ ├── smile_normal.png │ │ ├── smile_selected.png │ │ ├── text_normal.png │ │ ├── text_select.png │ │ └── wangge.jpg │ │ ├── drawable │ │ ├── border_shape.xml │ │ ├── border_shape_blue.xml │ │ ├── border_shape_focus.xml │ │ ├── border_shape_green.xml │ │ ├── border_shape_white.xml │ │ ├── draw_selector_bg.xml │ │ ├── ic_all_black.xml │ │ ├── ic_all_green.xml │ │ ├── ic_all_red.xml │ │ ├── ic_hot_black.xml │ │ ├── ic_hot_green.xml │ │ ├── ic_hot_red.xml │ │ ├── ic_jewelry_black.xml │ │ ├── ic_jewelry_green.xml │ │ ├── ic_jewelry_red.xml │ │ ├── ic_smile_black.xml │ │ ├── ic_smile_green.xml │ │ └── ic_smile_red.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── draw_navigation_bar_layout.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── lee │ └── drawlayoutsample │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 39 | 40 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 88 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 115 | 116 | 117 | 118 | 119 | 1.8 120 | 121 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DrawLayoutSample -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.3" 6 | defaultConfig { 7 | applicationId "com.lee.drawlayoutsample" 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 29 | testCompile 'junit:junit:4.12' 30 | } 31 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Android_Develop\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/lee/drawlayoutsample/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.lee.drawlayoutsample; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.lee.drawlayoutsample", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/assets/View coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/assets/View coordinates.png -------------------------------------------------------------------------------- /app/src/assets/all_black.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/all_green.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/all_red.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/hot_black.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/hot_green.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/hot_red.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/jewelry_black.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/jewelry_green.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/jewelry_red.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/line_black.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/line_green.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/line_red.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/smile_black.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/smile_green.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/assets/smile_red.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/lee/drawlayoutsample/App.java: -------------------------------------------------------------------------------- 1 | package com.lee.drawlayoutsample; 2 | 3 | import android.app.Application; 4 | 5 | import com.lee.drawlayoutsample.utils.BaseUtils; 6 | 7 | /** 8 | * Created by Lee on 2017/7/24. 9 | */ 10 | 11 | public class App extends Application { 12 | 13 | @Override 14 | public void onCreate() { 15 | super.onCreate(); 16 | BaseUtils.initialize(this); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/lee/drawlayoutsample/EditTextInfo.java: -------------------------------------------------------------------------------- 1 | package com.lee.drawlayoutsample; 2 | 3 | import android.os.Parcel; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * Created by Lee on 2016/11/4. 9 | */ 10 | 11 | public class EditTextInfo extends ViewInfo implements Serializable { 12 | 13 | public EditTextInfo(int id, float degree) { 14 | super(id, degree); 15 | } 16 | 17 | public String text; 18 | 19 | @Override 20 | public int describeContents() { 21 | return 0; 22 | } 23 | 24 | @Override 25 | public void writeToParcel(Parcel dest, int flags) { 26 | super.writeToParcel(dest, flags); 27 | dest.writeString(this.text); 28 | } 29 | 30 | protected EditTextInfo(Parcel in) { 31 | super(in); 32 | this.text = in.readString(); 33 | } 34 | 35 | public static final Creator CREATOR = new Creator() { 36 | @Override 37 | public EditTextInfo createFromParcel(Parcel source) { 38 | return new EditTextInfo(source); 39 | } 40 | 41 | @Override 42 | public EditTextInfo[] newArray(int size) { 43 | return new EditTextInfo[size]; 44 | } 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/lee/drawlayoutsample/ElementMemento.java: -------------------------------------------------------------------------------- 1 | package com.lee.drawlayoutsample; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by Lee on 2017/1/18. 7 | */ 8 | 9 | public class ElementMemento implements IElementMemento { 10 | 11 | private List mInfos; 12 | 13 | public ElementMemento(List mementos) { 14 | mInfos = mementos; 15 | } 16 | 17 | public List getInfos() { 18 | return mInfos; 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return "ElementMemento{" + 24 | "mInfos=" + mInfos + 25 | '}'; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/lee/drawlayoutsample/IElementMemento.java: -------------------------------------------------------------------------------- 1 | package com.lee.drawlayoutsample; 2 | 3 | /** 4 | * Created by Lee on 2017/1/18. 5 | */ 6 | 7 | public interface IElementMemento { 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/lee/drawlayoutsample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lee.drawlayoutsample; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.graphics.Bitmap; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.Paint; 11 | import android.graphics.Rect; 12 | import android.graphics.drawable.VectorDrawable; 13 | import android.os.Build; 14 | import android.os.Bundle; 15 | import android.support.v4.app.FragmentActivity; 16 | import android.text.TextUtils; 17 | import android.util.DisplayMetrics; 18 | import android.util.Log; 19 | import android.view.MotionEvent; 20 | import android.view.ScaleGestureDetector; 21 | import android.view.View; 22 | import android.view.Window; 23 | import android.view.animation.Animation; 24 | import android.view.animation.TranslateAnimation; 25 | import android.widget.EditText; 26 | import android.widget.FrameLayout; 27 | import android.widget.ImageView; 28 | import android.widget.RadioGroup; 29 | 30 | import com.lee.drawlayoutsample.utils.LogUtils; 31 | 32 | import java.io.FileNotFoundException; 33 | import java.io.FileOutputStream; 34 | import java.lang.reflect.Field; 35 | import java.util.ArrayList; 36 | import java.util.HashSet; 37 | import java.util.List; 38 | import java.util.Set; 39 | 40 | 41 | public class MainActivity extends FragmentActivity implements View.OnClickListener, RadioGroup.OnCheckedChangeListener { 42 | private static final String TAG = "MainActivity"; 43 | private final int DEGREE_0 = 0; 44 | private final int DEGREE_90 = 90; 45 | private final int DEGREE_180 = 180; 46 | private final int DEGREE_270 = 270; 47 | private ScaleGestureDetector mScaleGestureDetector; 48 | private ScaleGestureListener mScaleGestureListener = new ScaleGestureListener(); 49 | 50 | private Set mViewList = new HashSet(); 51 | private Set mFocusViewList = new HashSet(); 52 | private long downTime = 0; 53 | private float downX = 0f; 54 | private float downY = 0f; 55 | private float downImageX = 0f; 56 | private float downImageY = 0f; 57 | 58 | private float fdownX = 0f; 59 | private float fdownY = 0f; 60 | 61 | private int imageW = 0; 62 | private int imageH = 0; 63 | 64 | private float locationX = 0f; 65 | private float locationY = 0f; 66 | private DisplayMetrics mDisplayMetrics; 67 | private int mStatusBarHeight = 0; 68 | private FrameLayout mRootView; 69 | private ImageView mCurrentImageView; 70 | 71 | private Bitmap mLineBitmap; 72 | private Bitmap mLineBitmapBlack; 73 | private Bitmap mLineBitmapGreen; 74 | private Bitmap mLineBitmapRed; 75 | 76 | 77 | private List mInfoList = new ArrayList(); 78 | private FrameLayout mContent; 79 | private List mRectList; 80 | private float mCurrentScale = 1f; 81 | private float mContentDownX = 0f; 82 | private float mContentDownY = 0f; 83 | private boolean mMoved = false; 84 | private boolean mSelectTextTools; 85 | private ImageView mTextTools; 86 | private List mLefts = new ArrayList(); 87 | private List mTops = new ArrayList(); 88 | private List mRights = new ArrayList(); 89 | private List mBottoms = new ArrayList(); 90 | private List mLines = new ArrayList(); 91 | private static final int LIMIT = 50; 92 | private static final int OFFSET = 100; 93 | private static final int PADDING = 20; 94 | 95 | private int mCurrentColor; // 0--黑色,1--绿色,2--红色 96 | private MementoCreataker mCreataker; 97 | private MementoOriginator mOriginator; 98 | private int mCurrentIndex = -1; 99 | private FrameLayout.LayoutParams mDefaultEditTextParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); 100 | private FrameLayout.LayoutParams mContentLayoutParams; 101 | private int mRealInfoId = 5000; 102 | private float mStartX; 103 | private float mStartY; 104 | private static final int STROKE_WIDTH = 5; 105 | 106 | 107 | @Override 108 | protected void onCreate(Bundle savedInstanceState) { 109 | super.onCreate(savedInstanceState); 110 | requestWindowFeature(Window.FEATURE_NO_TITLE); 111 | setContentView(R.layout.activity_main); 112 | mOriginator = new MementoOriginator(); 113 | mCreataker = new MementoCreataker(); 114 | 115 | 116 | mDisplayMetrics = new DisplayMetrics(); 117 | getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics); 118 | mStatusBarHeight = getStatusBarHeight(this); 119 | mRootView = (FrameLayout) findViewById(R.id.root); 120 | mContent = (FrameLayout) findViewById(R.id.content); 121 | 122 | RadioGroup radioGroup = (RadioGroup) findViewById(R.id.radiogroup); 123 | radioGroup.setOnCheckedChangeListener(this); 124 | mContentLayoutParams = (FrameLayout.LayoutParams) mContent.getLayoutParams(); 125 | mContentLayoutParams.width = mDisplayMetrics.widthPixels - 312; 126 | mContentLayoutParams.height = mDisplayMetrics.heightPixels - mStatusBarHeight - 107; 127 | 128 | mContent.setOnTouchListener(new View.OnTouchListener() { 129 | @Override 130 | public boolean onTouch(View v, MotionEvent event) { 131 | mScaleGestureDetector.onTouchEvent(event); 132 | int action = event.getAction(); 133 | switch (action) { 134 | case MotionEvent.ACTION_DOWN: 135 | mContentDownX = event.getX(); 136 | mContentDownY = event.getY(); 137 | for (View view : mViewList) { 138 | if (((ViewInfo) view.getTag()).id == R.id.rectIcon) { 139 | Log.d(TAG, "rectIcon onDown..."); 140 | view.setBackgroundResource(R.drawable.border_shape); 141 | } else { 142 | Log.i(TAG, "not rectIcon onDown..."); 143 | view.setBackgroundColor(Color.TRANSPARENT); 144 | } 145 | if (((ViewInfo) view.getTag()).id == R.id.rectIcon) { 146 | view.setOnTouchListener(new MyTouchListener((ImageView) view)); 147 | int[] location = new int[2]; 148 | view.getLocationOnScreen(location); 149 | Rect rect = new Rect(); 150 | rect.left = location[0]; 151 | rect.right = location[0] + view.getWidth(); 152 | rect.top = location[1]; 153 | rect.bottom = location[1] + view.getHeight(); 154 | if (rect.contains((int) event.getX(), (int) event.getY())) { 155 | view.setBackgroundResource(R.drawable.border_shape_blue); 156 | } 157 | } 158 | } 159 | mFocusViewList.clear(); 160 | mMoved = true; 161 | return true; 162 | case MotionEvent.ACTION_MOVE: 163 | int w = mContent.getWidth(); 164 | int h = mContent.getHeight(); 165 | float maxW = (w * (mCurrentScale - 1f)) / 2; 166 | float maxH = (h * (mCurrentScale - 1f)) / 2; 167 | if (event.getPointerCount() <= 1 && mCurrentScale > 1f && mMoved) { 168 | Log.e(TAG, "move"); 169 | mMoved = true; 170 | float disX = mContentDownX - event.getX(); 171 | float disY = mContentDownY - event.getY(); 172 | if (((int) disY + mContent.getScrollY()) >= -maxH 173 | && ((int) disY + mContent.getScrollY()) <= maxH 174 | && ((int) disX + mContent.getScrollX()) >= -maxW 175 | && ((int) disX + mContent.getScrollX()) <= maxW) { 176 | mContent.scrollBy((int) disX, (int) disY); 177 | } 178 | mContentDownX = event.getX(); 179 | mContentDownY = event.getY(); 180 | return true; 181 | } else { 182 | Log.e(TAG, "not move"); 183 | mMoved = false; 184 | } 185 | return false; 186 | case MotionEvent.ACTION_UP: 187 | 188 | mMoved = false; 189 | 190 | if (mSelectTextTools) { 191 | 192 | float x = event.getRawX(); 193 | float y = event.getRawY(); 194 | Log.i(TAG, "x: " + x + "### y:" + y); 195 | 196 | if (x <= 312 || x >= 1920 || y <= 106 || y >= 1200) { 197 | Log.d(TAG, "不在画布区域内 x: " + x + "### y:" + y); 198 | return true; 199 | } else { 200 | EditText editText = new EditText(MainActivity.this); 201 | editText.requestFocus(); 202 | editText.setX(x - 312); 203 | editText.setY(y - 106); 204 | editText.setId(mViewList.size() + 1); 205 | ViewInfo viewInfo = new EditTextInfo(editText.getId(), 0); 206 | viewInfo.realId = ++mRealInfoId; 207 | viewInfo.type = ViewInfo.TYPE_EDITTEXT; 208 | viewInfo.x = editText.getX(); 209 | viewInfo.y = editText.getY(); 210 | editText.setTag(viewInfo); 211 | 212 | Log.d(TAG, "editTextId : " + editText.getId()); 213 | mViewList.add(editText); 214 | editText.setBackgroundResource(R.drawable.border_shape_focus); 215 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); 216 | mContent.addView(editText, layoutParams); 217 | createMemento(editText, false, true); 218 | 219 | } 220 | 221 | } 222 | return false; 223 | default: 224 | break; 225 | } 226 | return false; 227 | } 228 | }); 229 | 230 | mScaleGestureDetector = new ScaleGestureDetector(this, mScaleGestureListener); 231 | ImageView allImageView = (ImageView) findViewById(R.id.allIcon); 232 | allImageView.setOnTouchListener(mTouchListener); 233 | allImageView.setOnLongClickListener(mLongClickListener); 234 | ImageView smileImageView = (ImageView) findViewById(R.id.smileIcon); 235 | smileImageView.setOnTouchListener(mTouchListener); 236 | smileImageView.setOnLongClickListener(mLongClickListener); 237 | ImageView jewelryImageView = (ImageView) findViewById(R.id.jewelryIcon); 238 | jewelryImageView.setOnTouchListener(mTouchListener); 239 | jewelryImageView.setOnLongClickListener(mLongClickListener); 240 | ImageView hotImageView = (ImageView) findViewById(R.id.hotIcon); 241 | hotImageView.setOnTouchListener(mTouchListener); 242 | hotImageView.setOnLongClickListener(mLongClickListener); 243 | 244 | ImageView lineImageView = (ImageView) findViewById(R.id.lineIcon); 245 | lineImageView.setOnTouchListener(mTouchListener); 246 | lineImageView.setOnLongClickListener(mLongClickListener); 247 | ImageView rect = (ImageView) findViewById(R.id.rectIcon); 248 | rect.setOnTouchListener(mTouchListener); 249 | rect.setOnLongClickListener(mLongClickListener); 250 | 251 | 252 | findViewById(R.id.clear).setOnClickListener(this); 253 | findViewById(R.id.rotate).setOnClickListener(this); 254 | findViewById(R.id.delete).setOnClickListener(this); 255 | findViewById(R.id.copy).setOnClickListener(this); 256 | findViewById(R.id.cancel).setOnClickListener(this); 257 | findViewById(R.id.done).setOnClickListener(this); 258 | findViewById(R.id.back).setOnClickListener(this); 259 | findViewById(R.id.forward).setOnClickListener(this); 260 | mTextTools = (ImageView) findViewById(R.id.text); 261 | mTextTools.setOnClickListener(this); 262 | mViewList.clear(); 263 | 264 | layoutViews(); 265 | } 266 | 267 | private void layoutViews() { 268 | 269 | mViewList.clear(); 270 | mContent.removeAllViews(); 271 | 272 | if (mInfoList != null) { 273 | LogUtils.v("mInfoList : " + mInfoList.toString()); 274 | for (ViewInfo viewInfo : mInfoList) { 275 | 276 | ViewInfo newViewInfo; 277 | 278 | if (viewInfo.type == ViewInfo.TYPE_EDITTEXT) { 279 | newViewInfo = new EditTextInfo(viewInfo.id, viewInfo.degree); 280 | 281 | newViewInfo.type = ViewInfo.TYPE_EDITTEXT; 282 | String text = ((EditTextInfo) viewInfo).text; 283 | ((EditTextInfo) newViewInfo).text = text; 284 | EditText editText = new EditText(MainActivity.this); 285 | editText.setTag(newViewInfo); 286 | editText.setX(viewInfo.x); 287 | editText.setY(viewInfo.y); 288 | editText.setText(text); 289 | 290 | mViewList.add(editText); 291 | mContent.addView(editText, mDefaultEditTextParams); 292 | 293 | } else { 294 | newViewInfo = new ViewInfo(viewInfo.id, viewInfo.degree); 295 | newViewInfo.color = viewInfo.color; 296 | 297 | mContentLayoutParams = new FrameLayout.LayoutParams(viewInfo.width, viewInfo.height); 298 | 299 | ImageView imageView = new ImageView(MainActivity.this); 300 | imageView.setScaleType(ImageView.ScaleType.FIT_XY); 301 | imageView.setOnTouchListener(new MyTouchListener(imageView)); 302 | imageView.setTag(newViewInfo); 303 | imageView.setX(viewInfo.x); 304 | imageView.setY(viewInfo.y); 305 | setImageResource(imageView, false); 306 | if (newViewInfo.degree != 0) { 307 | imageView.setRotation(viewInfo.degree); 308 | } 309 | mViewList.add(imageView); 310 | mContent.addView(imageView, mContentLayoutParams); 311 | } 312 | newViewInfo.height = viewInfo.height; 313 | newViewInfo.width = viewInfo.width; 314 | newViewInfo.x = viewInfo.x; 315 | newViewInfo.y = viewInfo.y; 316 | newViewInfo.realId = viewInfo.realId; 317 | } 318 | } 319 | } 320 | 321 | 322 | @Override 323 | public void onDestroy() { 324 | super.onDestroy(); 325 | mRootView.removeAllViews(); 326 | 327 | recycle(mLineBitmap, mLineBitmapBlack, mLineBitmapGreen, mLineBitmapRed); 328 | } 329 | 330 | private void recycle(Bitmap... bitmaps) { 331 | if (bitmaps != null && bitmaps.length > 0) { 332 | for (Bitmap bitmap : bitmaps) { 333 | if (bitmap != null) bitmap.recycle(); 334 | } 335 | } 336 | } 337 | 338 | public void setImageResource(ImageView v, boolean focus) { 339 | ViewInfo viewInfo = (ViewInfo) v.getTag(); 340 | switch (viewInfo.id) { 341 | case R.id.allIcon: 342 | realSetImageResource(v, viewInfo, focus, R.drawable.all_selected, R.drawable.ic_all_black, R.drawable.ic_all_green, R.drawable.ic_all_red); 343 | break; 344 | case R.id.smileIcon: 345 | realSetImageResource(v, viewInfo, focus, R.drawable.smile_selected, R.drawable.ic_smile_black, R.drawable.ic_smile_green, R.drawable.ic_smile_red); 346 | break; 347 | case R.id.jewelryIcon: 348 | realSetImageResource(v, viewInfo, focus, R.drawable.jewelry_selected, R.drawable.ic_jewelry_black, R.drawable.ic_jewelry_green, R.drawable.ic_jewelry_red); 349 | break; 350 | case R.id.hotIcon: 351 | realSetImageResource(v, viewInfo, focus, R.drawable.hot_selected, R.drawable.ic_hot_black, R.drawable.ic_hot_green, R.drawable.ic_hot_red); 352 | break; 353 | 354 | case R.id.lineIcon: 355 | 356 | if (mLineBitmap == null) { 357 | 358 | mLineBitmapBlack = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 359 | mLineBitmapGreen = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 360 | mLineBitmapRed = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 361 | 362 | Paint paint = new Paint(); 363 | paint.setColor(Color.BLACK); 364 | paint.setStrokeWidth(STROKE_WIDTH); 365 | 366 | Canvas canvas = new Canvas(mLineBitmapBlack); 367 | canvas.drawLine(0, 50, 100, 50, paint); 368 | 369 | paint.setColor(Color.RED); 370 | canvas = new Canvas(mLineBitmapRed); 371 | canvas.drawLine(0, 50, 100, 50, paint); 372 | 373 | paint.setColor(Color.GREEN); 374 | canvas = new Canvas(mLineBitmapGreen); 375 | canvas.drawLine(0, 50, 100, 50, paint); 376 | 377 | mLineBitmap = mLineBitmapBlack; 378 | 379 | if (viewInfo.color == 2) { 380 | mLineBitmap = mLineBitmapRed; 381 | } else if (mCurrentColor == 1) { 382 | mLineBitmap = mLineBitmapGreen; 383 | } 384 | } 385 | if (focus) { 386 | v.setImageResource(R.drawable.line_selected); 387 | } else { 388 | v.setImageBitmap(mLineBitmap); 389 | } 390 | break; 391 | 392 | case R.id.rectIcon: 393 | if (focus) { 394 | v.setBackgroundResource(R.drawable.border_shape_focus); 395 | } else { 396 | v.setBackgroundResource(R.drawable.border_shape); 397 | } 398 | break; 399 | default: 400 | break; 401 | } 402 | } 403 | 404 | 405 | private void realSetImageResource(ImageView v, ViewInfo viewInfo, boolean focus, int focusId, int blackId, int greenId, int redId) { 406 | if (focus) { 407 | v.setImageResource(focusId); 408 | } else { 409 | if (viewInfo.color == 2) { 410 | v.setImageResource(redId); 411 | } else if (viewInfo.color == 1) { 412 | v.setImageResource(greenId); 413 | } else { 414 | v.setImageResource(blackId); 415 | } 416 | } 417 | } 418 | 419 | 420 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 421 | private Bitmap getBitmap(VectorDrawable vectorDrawable) { 422 | Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), 423 | vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); 424 | Canvas canvas = new Canvas(bitmap); 425 | vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 426 | vectorDrawable.draw(canvas); 427 | return bitmap; 428 | } 429 | 430 | @Override 431 | public void onClick(View v) { 432 | switch (v.getId()) { 433 | case R.id.rotate: 434 | for (View view : mFocusViewList) { 435 | LogUtils.d("start x: " + view.getX() + ", y :" + view.getY()); 436 | ViewInfo viewInfo = (ViewInfo) view.getTag(); 437 | if (viewInfo.id == R.id.lineIcon || viewInfo.id == R.id.xuxianIcon || viewInfo.id == R.id.rectIcon) { 438 | FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams(); 439 | params.width = view.getHeight(); 440 | params.height = view.getWidth(); 441 | view.requestLayout(); 442 | } 443 | float degree = view.getRotation() + 90f; 444 | LogUtils.d("degree: " + degree); 445 | if (degree >= 360) { 446 | degree = 0; 447 | } 448 | view.setRotation(degree); 449 | LogUtils.d("end x: " + view.getX() + ", y :" + view.getY()); 450 | createMemento(view, false, false); 451 | 452 | } 453 | break; 454 | case R.id.clear: 455 | for (View view : mViewList) { 456 | mContent.removeView(view); 457 | } 458 | mViewList.clear(); 459 | mContent.requestLayout(); 460 | break; 461 | case R.id.delete: 462 | for (View view : new ArrayList(mFocusViewList)) { 463 | removeViews(view); 464 | createMemento(view, true, false); 465 | } 466 | mContent.requestLayout(); 467 | break; 468 | case R.id.copy: 469 | LogUtils.i("mFocusViewList size: " + mFocusViewList.size()); 470 | for (View view : mFocusViewList) { 471 | copyView(view); 472 | if (((ViewInfo) view.getTag()).id == R.id.rectIcon) { 473 | int[] location = new int[2]; 474 | view.getLocationOnScreen(location); 475 | int x = location[0]; 476 | int y = location[1]; 477 | Rect srcRect = new Rect(); 478 | srcRect.left = x; 479 | srcRect.right = x + view.getWidth(); 480 | srcRect.top = y; 481 | srcRect.bottom = y + view.getHeight(); 482 | List rectList = getNeedMoveView(srcRect, (ImageView) view); 483 | for (View view1 : rectList) { 484 | copyView(view1); 485 | } 486 | } 487 | } 488 | break; 489 | case R.id.cancel: 490 | setResult(Activity.RESULT_CANCELED); 491 | finish(); 492 | break; 493 | case R.id.done: 494 | 495 | 496 | Bitmap bitmap = captureScreen(); 497 | FileOutputStream outputStream = null; 498 | if (bitmap != null) { 499 | try { 500 | outputStream = new FileOutputStream(getExternalCacheDir() + "/draw.jpg"); 501 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); 502 | } catch (FileNotFoundException e) { 503 | e.printStackTrace(); 504 | } finally { 505 | try { 506 | if (outputStream != null) { 507 | outputStream.close(); 508 | } 509 | } catch (Exception e) { 510 | Log.e(TAG, e.getMessage(), e); 511 | } 512 | 513 | } 514 | } 515 | 516 | 517 | mContent.setScaleX(1f); 518 | mContent.setScaleY(1f); 519 | mInfoList.clear(); 520 | for (View view : mViewList) { 521 | 522 | ViewInfo viewInfo = (ViewInfo) view.getTag(); 523 | ViewInfo newViewInfo; 524 | 525 | if (viewInfo.type == ViewInfo.TYPE_EDITTEXT) { 526 | String string = ((EditText) view).getText().toString(); 527 | if (TextUtils.isEmpty(string)) { 528 | continue; 529 | } 530 | newViewInfo = new EditTextInfo(viewInfo.id, viewInfo.degree); 531 | ((EditTextInfo) newViewInfo).text = ((EditText) view).getText().toString(); 532 | newViewInfo.type = ViewInfo.TYPE_EDITTEXT; 533 | } else { 534 | newViewInfo = new ViewInfo(viewInfo.id, viewInfo.degree); 535 | } 536 | newViewInfo.color = viewInfo.color; 537 | newViewInfo.width = view.getWidth(); 538 | newViewInfo.height = view.getHeight(); 539 | int[] location = new int[2]; 540 | view.getLocationOnScreen(location); 541 | newViewInfo.x = location[0]; 542 | newViewInfo.y = location[1]; 543 | 544 | mInfoList.add(newViewInfo); 545 | } 546 | Log.d(TAG, "end mInfoList : " + mInfoList.toString()); 547 | 548 | finish(); 549 | break; 550 | case R.id.text: 551 | mSelectTextTools = !mSelectTextTools; 552 | if (mSelectTextTools) { 553 | mTextTools.setImageResource(R.drawable.text_select); 554 | } else { 555 | mTextTools.setImageResource(R.drawable.text_normal); 556 | if (mViewList != null && !mViewList.isEmpty()) { 557 | ArrayList removeViews = new ArrayList<>(); 558 | for (View view : mViewList) { 559 | if (view instanceof EditText) { 560 | String string = ((EditText) view).getText().toString(); 561 | if (TextUtils.isEmpty(string)) { 562 | removeViews.add(view); 563 | mContent.removeView(view); 564 | LogUtils.d("移除未输入内容的EditText..."); 565 | } 566 | } 567 | } 568 | if (!removeViews.isEmpty()) { 569 | mViewList.removeAll(removeViews); 570 | } 571 | } 572 | } 573 | break; 574 | case R.id.back: 575 | --mCurrentIndex; 576 | ElementMemento memento = mCreataker.restoreMemento(mCurrentIndex); 577 | if (memento == null) { 578 | ++mCurrentIndex; 579 | } else { 580 | mInfoList.clear(); 581 | mInfoList.addAll(mOriginator.restoreMemento(memento)); 582 | mOriginator.printMementos(); 583 | layoutViews(); 584 | } 585 | LogUtils.d("back mCurrentIndex: " + mCurrentIndex); 586 | break; 587 | case R.id.forward: 588 | ++mCurrentIndex; 589 | memento = mCreataker.restoreMemento(mCurrentIndex); 590 | if (memento == null) { 591 | --mCurrentIndex; 592 | } else { 593 | mInfoList.clear(); 594 | mInfoList.addAll(mOriginator.restoreMemento(memento)); 595 | layoutViews(); 596 | } 597 | LogUtils.d("forward mCurrentIndex: " + mCurrentIndex); 598 | break; 599 | 600 | default: 601 | break; 602 | } 603 | } 604 | 605 | private void removeViews(View view) { 606 | mContent.removeView(view); 607 | mViewList.remove(view); 608 | mFocusViewList.remove(view); 609 | } 610 | 611 | 612 | @Override 613 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 614 | super.onActivityResult(requestCode, resultCode, data); 615 | } 616 | 617 | public static int getStatusBarHeight(Context context) { 618 | Class c = null; 619 | Object obj = null; 620 | Field field = null; 621 | int x = 0, statusBarHeight = 0; 622 | try { 623 | c = Class.forName("com.android.internal.R$dimen"); 624 | obj = c.newInstance(); 625 | field = c.getField("status_bar_height"); 626 | x = Integer.parseInt(field.get(obj).toString()); 627 | statusBarHeight = context.getResources().getDimensionPixelSize(x); 628 | } catch (Exception e1) { 629 | e1.printStackTrace(); 630 | } 631 | return statusBarHeight; 632 | } 633 | 634 | private View.OnTouchListener mTouchListener = new View.OnTouchListener() { 635 | @Override 636 | public boolean onTouch(View v, final MotionEvent event) { 637 | int action = event.getAction(); 638 | if (mCurrentImageView == null && MotionEvent.ACTION_DOWN != action) { 639 | return false; 640 | } 641 | 642 | switch (action) { 643 | case MotionEvent.ACTION_DOWN: 644 | fdownX = event.getX(); 645 | fdownY = event.getY(); 646 | LogUtils.d("fdownX: " + fdownX + " ###fdownY: " + fdownY); 647 | getLineCoordinate(); 648 | break; 649 | case MotionEvent.ACTION_MOVE: 650 | float disX = event.getX() - fdownX - OFFSET; 651 | float disY = event.getY() - fdownY - OFFSET; 652 | LogUtils.d("disX: " + disX + " ### disY: " + disY + " ### getX: " + event.getX() + " ### getY: " + event.getY()); 653 | mCurrentImageView.setX(mCurrentImageView.getX() + disX); 654 | mCurrentImageView.setY(mCurrentImageView.getY() + disY); 655 | LogUtils.i("mCurrentImageView.getX(): " + mCurrentImageView.getX() + " ### mCurrentImageView.getY(): " + mCurrentImageView.getY()); 656 | fdownX = event.getX() - OFFSET; 657 | fdownY = event.getY() - OFFSET; 658 | LogUtils.v("fdownX: " + fdownX + " ### fdownY: " + fdownY); 659 | 660 | imageW = mCurrentImageView.getWidth(); 661 | imageH = mCurrentImageView.getHeight(); 662 | 663 | mCurrentImageView.setBackgroundResource(android.R.color.transparent); 664 | setImageResource(mCurrentImageView, true); 665 | 666 | return true; 667 | case MotionEvent.ACTION_UP: 668 | float x = mCurrentImageView.getX(); 669 | float y = mCurrentImageView.getY(); 670 | if (x <= 212) { 671 | cancelMoveView(x, y); 672 | return true; 673 | } else { 674 | if (x > 212 && x < 312) { 675 | x = 312; 676 | } else if (x > (mDisplayMetrics.widthPixels - 100)) { 677 | x = mDisplayMetrics.widthPixels - 100; 678 | } 679 | 680 | if (y <= 106) { 681 | y = 106; 682 | } else if (y > mDisplayMetrics.heightPixels - 100 - mStatusBarHeight) { 683 | y = mDisplayMetrics.heightPixels - 100 - mStatusBarHeight; 684 | } 685 | mCurrentImageView.setX(x - 312); 686 | mCurrentImageView.setY(y - 107); 687 | setImageResource(mCurrentImageView, false); 688 | mRootView.removeView(mCurrentImageView); 689 | mContent.addView(mCurrentImageView); 690 | 691 | createMemento(mCurrentImageView, false, true); 692 | 693 | if (((ViewInfo) mCurrentImageView.getTag()).id == R.id.rectIcon) { 694 | mCurrentImageView.setBackgroundResource(R.drawable.border_shape); 695 | } else { 696 | mCurrentImageView.setBackgroundResource(android.R.color.transparent); 697 | } 698 | } 699 | mCurrentImageView = null; 700 | break; 701 | case MotionEvent.ACTION_CANCEL: 702 | float x1 = mCurrentImageView.getX(); 703 | float y1 = mCurrentImageView.getY(); 704 | cancelMoveView(x1, y1); 705 | break; 706 | } 707 | return false; 708 | } 709 | }; 710 | 711 | 712 | private void createMemento(View view, boolean isDelete, boolean isNewElement) { 713 | 714 | if (mInfoList == null) { 715 | mInfoList = new ArrayList<>(); 716 | } 717 | mCurrentIndex++; 718 | 719 | // LogUtils.d("add mCurrentIndex: " + mCurrentIndex); 720 | 721 | ViewInfo viewInfo = (ViewInfo) view.getTag(); 722 | // LogUtils.d("viewInfo: " + viewInfo); 723 | if (isNewElement) { //刚刚从侧边栏拖拽出来的元素 724 | // LogUtils.d("新增元素"); 725 | viewInfo.width = viewInfo.width == 0 ? view.getWidth() : viewInfo.width; 726 | viewInfo.height = viewInfo.height == 0 ? view.getHeight() : viewInfo.height; 727 | viewInfo.x = viewInfo.x == 0 ? (int) view.getX() : viewInfo.x; 728 | viewInfo.y = viewInfo.y == 0 ? (int) view.getY() : viewInfo.y; 729 | mInfoList.add(viewInfo); 730 | } else { //改变现有元素 731 | // LogUtils.d("改变现有元素"); 732 | if (isDelete) { 733 | // LogUtils.i("删除元素"); 734 | for (int i = mInfoList.size() - 1; i >= 0; i--) { 735 | ViewInfo info = mInfoList.get(i); 736 | if (info.id == viewInfo.id && info.realId == viewInfo.realId) { 737 | // LogUtils.i("删除重复元素: " + info); 738 | mInfoList.remove(info); 739 | } 740 | } 741 | } else { 742 | ViewInfo newViewInfo = new ViewInfo(viewInfo.id, view.getRotation()); 743 | newViewInfo.realId = ++mRealInfoId; 744 | newViewInfo.width = view.getWidth(); 745 | newViewInfo.height = view.getHeight(); 746 | newViewInfo.x = view.getX(); 747 | newViewInfo.y = view.getY(); 748 | newViewInfo.type = viewInfo.type; 749 | newViewInfo.color = mCurrentColor; 750 | 751 | for (int i = mInfoList.size() - 1; i >= 0; i--) { 752 | ViewInfo info = mInfoList.get(i); 753 | if (info.id == viewInfo.id && info.realId == viewInfo.realId) { 754 | // LogUtils.i("删除重复元素: " + info); 755 | mInfoList.remove(info); 756 | view.setTag(newViewInfo); 757 | } 758 | } 759 | mInfoList.add(newViewInfo); 760 | } 761 | } 762 | 763 | List infos = new ArrayList<>(); 764 | 765 | infos.addAll(mInfoList); 766 | mOriginator.setInfos(infos); 767 | mCreataker.createMemento(mOriginator.createMemento(), mCurrentIndex); 768 | 769 | } 770 | 771 | private void cancelMoveView(float x, float y) { 772 | TranslateAnimation translateAnimation = new TranslateAnimation(0, locationX - x, 0, locationY - y); 773 | translateAnimation.setDuration(200); 774 | translateAnimation.setAnimationListener(new Animation.AnimationListener() { 775 | @Override 776 | public void onAnimationStart(Animation animation) { 777 | } 778 | 779 | @Override 780 | public void onAnimationEnd(Animation animation) { 781 | mRootView.removeView(mCurrentImageView); 782 | } 783 | 784 | @Override 785 | public void onAnimationRepeat(Animation animation) { 786 | 787 | } 788 | }); 789 | mCurrentImageView.startAnimation(translateAnimation); 790 | } 791 | 792 | private View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() { 793 | @Override 794 | public boolean onLongClick(View v) { 795 | final ImageView imageView = new ImageView(MainActivity.this); 796 | mCurrentImageView = imageView; 797 | ViewInfo viewInfo = new ViewInfo(v.getId(), 0); 798 | viewInfo.type = ViewInfo.TYPE_IMAGEVIEW; 799 | viewInfo.color = mCurrentColor; 800 | viewInfo.realId = ++mRealInfoId; 801 | imageView.setTag(viewInfo); 802 | imageView.setScaleType(ImageView.ScaleType.FIT_XY); 803 | setImageResource(imageView, true); 804 | int[] location = new int[2]; 805 | v.getLocationOnScreen(location); 806 | locationX = location[0]; 807 | locationY = location[1]; 808 | imageView.setX(locationX + 5); 809 | imageView.setY(locationY + 5); 810 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(v.getWidth(), v.getHeight()); 811 | mRootView.addView(imageView, params); 812 | mViewList.add(imageView); 813 | imageView.setOnTouchListener(new MyTouchListener(imageView)); 814 | return true; 815 | } 816 | }; 817 | 818 | 819 | @Override 820 | public void onCheckedChanged(RadioGroup radioGroup, int checkedId) { 821 | 822 | switch (checkedId) { 823 | case R.id.rb_black: 824 | mCurrentColor = 0; 825 | mLineBitmap = mLineBitmapBlack; 826 | 827 | break; 828 | case R.id.rb_green: 829 | mCurrentColor = 1; 830 | mLineBitmap = mLineBitmapGreen; 831 | 832 | break; 833 | case R.id.rb_red: 834 | mCurrentColor = 2; 835 | mLineBitmap = mLineBitmapRed; 836 | 837 | break; 838 | default: 839 | LogUtils.e("default checkedId: " + checkedId, new IllegalArgumentException()); 840 | break; 841 | } 842 | for (View view : mFocusViewList) { 843 | if (view instanceof ImageView) { 844 | createMemento(view, false, false); 845 | setImageResource((ImageView) view, false); 846 | } 847 | } 848 | } 849 | 850 | 851 | private class MyTouchListener implements View.OnTouchListener { 852 | private ImageView imageView; 853 | 854 | MyTouchListener(ImageView imageView) { 855 | this.imageView = imageView; 856 | } 857 | 858 | @Override 859 | public boolean onTouch(View v, MotionEvent event) { 860 | int action = event.getAction(); 861 | ViewInfo viewInfo = (ViewInfo) v.getTag(); 862 | 863 | float targetX = 0; 864 | float targetY = 0; 865 | float targetRight = 0; 866 | float targetBottom = 0; 867 | 868 | switch (action) { 869 | case MotionEvent.ACTION_DOWN: 870 | mStartX = imageView.getX(); 871 | mStartY = imageView.getY(); 872 | downX = event.getX(); 873 | downY = event.getY(); 874 | if (v.getRotation() == DEGREE_90) { 875 | LogUtils.d("90 "); 876 | downX = v.getHeight() - event.getY(); 877 | downY = v.getWidth() - (v.getWidth() - event.getX()); 878 | } else if (v.getRotation() == DEGREE_180) { 879 | LogUtils.d("180 "); 880 | downX = v.getWidth() - event.getX(); 881 | downY = v.getHeight() - event.getY(); 882 | } else if (v.getRotation() == DEGREE_270) { 883 | LogUtils.d("270 "); 884 | downX = event.getY(); 885 | downY = v.getWidth() - event.getX(); 886 | } 887 | LogUtils.d("downX: " + downX + " , downY: " + downY); 888 | downTime = System.currentTimeMillis(); 889 | 890 | imageW = imageView.getWidth(); 891 | imageH = imageView.getHeight(); 892 | 893 | int[] location0 = new int[2]; 894 | imageView.getLocationOnScreen(location0); 895 | int x0 = location0[0]; 896 | int y0 = location0[1]; 897 | Rect srcRect = new Rect(); 898 | srcRect.left = x0; 899 | srcRect.right = x0 + imageView.getWidth(); 900 | srcRect.top = y0; 901 | srcRect.bottom = y0 + imageView.getHeight(); 902 | 903 | getLineCoordinate(); 904 | 905 | if (viewInfo.id == R.id.rectIcon) { 906 | mRectList = getNeedMoveView(srcRect, imageView); 907 | } 908 | return true; 909 | case MotionEvent.ACTION_MOVE: 910 | float x1 = event.getX(); 911 | float y1 = event.getY(); 912 | 913 | if (v.getRotation() == DEGREE_90) { 914 | x1 = v.getHeight() - event.getY(); 915 | y1 = v.getWidth() - (v.getWidth() - event.getX()); 916 | } else if (v.getRotation() == DEGREE_180) { 917 | x1 = v.getWidth() - event.getX(); 918 | y1 = v.getHeight() - event.getY(); 919 | } else if (v.getRotation() == DEGREE_270) { 920 | x1 = event.getY(); 921 | y1 = v.getWidth() - event.getX(); 922 | } 923 | 924 | float disX = x1 - downX; 925 | float disY = y1 - downY; 926 | 927 | LogUtils.i("disX : " + (int) disX + ", disY : " + (int) disY); 928 | 929 | if ((viewInfo.id == R.id.lineIcon || viewInfo.id == R.id.xuxianIcon) && mFocusViewList.contains(imageView)) { 930 | FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) imageView.getLayoutParams(); 931 | params.width = imageW + (int) disX; 932 | params.height = imageH + (int) disY; 933 | imageView.requestLayout(); 934 | } else if (viewInfo.id == R.id.rectIcon && mFocusViewList.contains(imageView)) { 935 | FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) imageView.getLayoutParams(); 936 | params.width = imageW + (int) disX; 937 | if (params.width < 5) { 938 | params.width = 5; 939 | } 940 | params.height = imageH + (int) disY; 941 | if (params.height < 5) { 942 | params.height = 5; 943 | } 944 | imageView.requestLayout(); 945 | } else { 946 | if (viewInfo.id == R.id.rectIcon) { 947 | imageView.setX(imageView.getX() + disX); 948 | imageView.setY(imageView.getY() + disY); 949 | if (mRectList != null) { 950 | for (View view : mRectList) { 951 | view.setX(view.getX() + disX); 952 | view.setY(view.getY() + disY); 953 | } 954 | } 955 | } else { 956 | if (mFocusViewList.contains(imageView)) { 957 | FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) imageView.getLayoutParams(); 958 | params.width = imageW + (int) disX; 959 | params.height = imageH + (int) disY; 960 | imageView.requestLayout(); 961 | } else { //处理图标拖动 962 | 963 | imageView.setX(imageView.getX() + disX - OFFSET); 964 | imageView.setY(imageView.getY() + disY - OFFSET); 965 | imageView.setBackgroundResource(android.R.color.transparent); 966 | 967 | targetX = imageView.getX(); 968 | targetY = imageView.getY(); 969 | targetRight = targetX + imageW; 970 | targetBottom = targetY + imageH; 971 | // LogUtils.d("ACTION_MOVE " + (int) targetX + "," + (int) targetY + "," + (int) targetRight + "," + (int) targetBottom); 972 | 973 | for (int i = 0; i < mTops.size(); i++) { 974 | float consultTop = mTops.get(i); 975 | View line = mLines.get(i); 976 | ViewInfo lineInfo = (ViewInfo) line.getTag(); 977 | if ((lineInfo.degree == 90 || lineInfo.degree == 270) && Math.abs(consultTop - targetBottom) <= LIMIT) { //在竖线上面 978 | int lineMiddleX = (2 * mRights.get(i) - line.getHeight()) / 2;//获取到线条的y/2 979 | if (disX >= 0) { //从左往右 980 | if (Math.abs(mLefts.get(i) - targetRight) <= OFFSET) { 981 | imageView.setX(lineMiddleX - imageW / 2); 982 | imageView.setY(consultTop - imageH + PADDING); 983 | imageView.setBackgroundResource(R.drawable.border_shape_green); 984 | Log.d(TAG, "竖线上边匹配成功 从左往右 targetBottom : " + targetBottom + ", consultTop: " + consultTop); 985 | } 986 | } else { 987 | if (Math.abs(targetX - mRights.get(i)) <= OFFSET) { 988 | imageView.setX(lineMiddleX - imageW / 2); 989 | imageView.setY(consultTop - imageH + PADDING); 990 | imageView.setBackgroundResource(R.drawable.border_shape_green); 991 | Log.d(TAG, "竖线上边匹配成功 从右往左 targetBottom : " + targetBottom + ", consultTop: " + consultTop); 992 | } 993 | } 994 | } 995 | } 996 | 997 | for (int i = 0; i < mBottoms.size(); i++) { 998 | float consultBottom = mBottoms.get(i); 999 | View line = mLines.get(i); 1000 | ViewInfo lineInfo = (ViewInfo) line.getTag(); 1001 | if ((lineInfo.degree == 90 || lineInfo.degree == 270) && Math.abs(targetY - consultBottom) <= LIMIT) { 1002 | int lineMiddleX = (2 * mRights.get(i) - line.getHeight()) / 2;//获取到线条的y/2 1003 | if (disX >= 0) { //从左往右 1004 | if (Math.abs(mLefts.get(i) - targetRight) <= OFFSET) { 1005 | imageView.setX(lineMiddleX - imageW / 2); 1006 | imageView.setY(consultBottom - PADDING); 1007 | imageView.setBackgroundResource(R.drawable.border_shape_green); 1008 | Log.i(TAG, "竖线下边匹配成功 从左往右 targetY : " + targetY + ", consultBottom : " + consultBottom); 1009 | } 1010 | } else { 1011 | if (Math.abs(targetX - mRights.get(i)) <= OFFSET) { 1012 | imageView.setX(lineMiddleX - imageW / 2); 1013 | imageView.setY(consultBottom - PADDING); 1014 | imageView.setBackgroundResource(R.drawable.border_shape_green); 1015 | Log.i(TAG, "竖线下边匹配成功 从右往左 targetY : " + targetY + ", consultBottom : " + consultBottom); 1016 | } 1017 | } 1018 | } 1019 | } 1020 | 1021 | for (int i = 0; i < mLefts.size(); i++) { 1022 | float consultLeft = mLefts.get(i); 1023 | View line = mLines.get(i); 1024 | ViewInfo lineInfo = (ViewInfo) line.getTag(); 1025 | if ((lineInfo.degree == 0 || lineInfo.degree == 180) && Math.abs(consultLeft - targetRight) <= LIMIT) { 1026 | int lineMiddleY = (2 * mBottoms.get(i) - line.getHeight()) / 2;//获取到线条的y/2 1027 | int newY = lineMiddleY - imageH / 2; 1028 | if (disY >= 0) { //从上到下 1029 | if (Math.abs(mTops.get(i) - targetBottom) <= OFFSET) { 1030 | imageView.setX(consultLeft - imageW + PADDING); 1031 | imageView.setY(newY); 1032 | imageView.setBackgroundResource(R.drawable.border_shape_green); 1033 | Log.d(TAG, "横线左边匹配成功 从上往下 targetRight : " + targetRight + " , consultLeft: " + consultLeft); 1034 | } 1035 | } else { 1036 | if (Math.abs(targetY - mBottoms.get(i)) <= OFFSET) { 1037 | imageView.setX(consultLeft - imageW + PADDING); 1038 | imageView.setY(newY); 1039 | imageView.setBackgroundResource(R.drawable.border_shape_green); 1040 | Log.d(TAG, "横线左边匹配成功 从下往上 targetRight : " + targetRight + " , consultLeft: " + consultLeft); 1041 | } 1042 | } 1043 | } 1044 | } 1045 | 1046 | for (int i = 0; i < mRights.size(); i++) { 1047 | float consultRight = mRights.get(i); 1048 | View line = mLines.get(i); 1049 | ViewInfo lineInfo = (ViewInfo) line.getTag(); 1050 | if ((lineInfo.degree == 0 || lineInfo.degree == 180) && Math.abs(targetX - consultRight) <= LIMIT) { 1051 | int lineMiddleY = (2 * mBottoms.get(i) - line.getHeight()) / 2;//获取到线条的y/2 1052 | int newY = lineMiddleY - imageH / 2; 1053 | Log.d(TAG, "lineMiddleY : " + lineMiddleY + ", newY: " + newY); 1054 | if (disY >= 0) { //从上往下 1.如果targetBottom - con 1055 | if (Math.abs(mTops.get(i) - targetBottom) <= OFFSET) { 1056 | imageView.setX(consultRight - PADDING); 1057 | imageView.setY(newY); 1058 | imageView.setBackgroundResource(R.drawable.border_shape_green); 1059 | Log.d(TAG, "横线右边匹配成功 从上往下 targetX : " + targetX + " , consultRight: " + consultRight); 1060 | } 1061 | } else { 1062 | if (Math.abs(targetY - mBottoms.get(i)) <= OFFSET) { 1063 | imageView.setX(consultRight - PADDING); 1064 | imageView.setY(newY); 1065 | imageView.setBackgroundResource(R.drawable.border_shape_green); 1066 | Log.d(TAG, "横线右边匹配成功 从下往上 targetX : " + targetX + " , consultRight: " + consultRight); 1067 | } 1068 | } 1069 | 1070 | } 1071 | } 1072 | 1073 | } 1074 | } 1075 | } 1076 | return true; 1077 | case MotionEvent.ACTION_UP: 1078 | float x = imageView.getX(); 1079 | float y = imageView.getY(); 1080 | 1081 | if (System.currentTimeMillis() - downTime > 1500) { 1082 | //it's long click 1083 | } else { 1084 | if (viewInfo.id == R.id.rectIcon && mRectList != null && !mRectList.isEmpty()) { 1085 | boolean hasFocusOtherView = false; 1086 | for (View view : mRectList) { 1087 | int[] location = new int[2]; 1088 | view.getLocationOnScreen(location); 1089 | Rect rect = new Rect(); 1090 | rect.left = location[0]; 1091 | rect.right = location[0] + view.getWidth(); 1092 | rect.top = location[1]; 1093 | rect.bottom = location[1] + view.getHeight(); 1094 | 1095 | imageView.getLocationOnScreen(location); 1096 | int pX = location[0] + (int) event.getX(); 1097 | int pY = location[1] + (int) event.getY(); 1098 | if (rect.contains(pX, pY)) { 1099 | hasFocusOtherView = true; 1100 | mFocusViewList.add(view); 1101 | view.setBackgroundResource(R.drawable.border_shape_blue); 1102 | imageView.setBackgroundResource(R.drawable.border_shape); 1103 | mFocusViewList.remove(imageView); 1104 | imageView.setOnTouchListener(null); 1105 | break; 1106 | } 1107 | } 1108 | 1109 | if (!hasFocusOtherView) { 1110 | imageView.setOnTouchListener(new MyTouchListener(imageView)); 1111 | mFocusViewList.add(imageView); 1112 | imageView.setBackgroundResource(R.drawable.border_shape_blue); 1113 | if (mRectList != null) { 1114 | for (View view : mRectList) { 1115 | view.setBackgroundColor(Color.TRANSPARENT); 1116 | mFocusViewList.remove(view); 1117 | } 1118 | } 1119 | imageView.requestLayout(); 1120 | } 1121 | // removeViews(v); 1122 | } else { 1123 | mFocusViewList.add(imageView); 1124 | if (Math.abs(x - downImageX) <= 5 && Math.abs(y - downImageY) <= 5) { 1125 | imageView.setBackgroundResource(R.drawable.border_shape_blue); 1126 | } 1127 | } 1128 | } 1129 | if (x < 0) { 1130 | x = 0; 1131 | imageView.setX(x); 1132 | } else if (x > (mDisplayMetrics.widthPixels - 312 - 100)) { 1133 | x = mDisplayMetrics.widthPixels - 100 - 312; 1134 | imageView.setX(x); 1135 | } 1136 | 1137 | if (y <= 0) { 1138 | y = 0; 1139 | imageView.setY(y); 1140 | } else if (y > mDisplayMetrics.heightPixels - 100 - mStatusBarHeight - 107) { 1141 | y = mDisplayMetrics.heightPixels - 100 - mStatusBarHeight - 107; 1142 | imageView.setY(y); 1143 | } 1144 | 1145 | if (Math.abs(mStartX - x) >= 3 || Math.abs(mStartY - y) >= 3) { 1146 | createMemento(imageView, false, false); 1147 | } 1148 | 1149 | if (mRectList != null) { 1150 | mRectList.clear(); 1151 | } 1152 | return true; 1153 | } 1154 | return false; 1155 | } 1156 | } 1157 | 1158 | 1159 | private void getLineCoordinate() { 1160 | mLefts.clear(); 1161 | mTops.clear(); 1162 | mRights.clear(); 1163 | mBottoms.clear(); 1164 | mLines.clear(); 1165 | for (View view : mViewList) { 1166 | ViewInfo viewInfo = (ViewInfo) view.getTag(); 1167 | if (R.id.lineIcon == viewInfo.id || R.id.xuxianIcon == viewInfo.id) { 1168 | mLines.add(view); 1169 | 1170 | int x = (int) view.getX(); 1171 | int y = (int) view.getY(); 1172 | int bottom = y + view.getHeight(); 1173 | int right = x + view.getWidth(); 1174 | 1175 | mLefts.add(x); 1176 | mTops.add(y); 1177 | mRights.add(right); 1178 | mBottoms.add(bottom); 1179 | } 1180 | } 1181 | 1182 | Log.d(TAG, "mLefts : " + mLefts.toString()); 1183 | Log.i(TAG, "mTops : " + mTops.toString()); 1184 | Log.d(TAG, "mRights : " + mRights.toString()); 1185 | Log.i(TAG, "mBottoms : " + mBottoms.toString()); 1186 | 1187 | } 1188 | 1189 | public Bitmap captureScreen() { 1190 | // 允许当前窗口保存缓存信息 1191 | for (int i = 0; i < mContent.getChildCount(); i++) { 1192 | View view = mContent.getChildAt(i); 1193 | if (view instanceof EditText) { 1194 | ((EditText) view).setCursorVisible(false); 1195 | } 1196 | view.clearFocus(); 1197 | view.setBackgroundResource(android.R.color.transparent); 1198 | } 1199 | mContent.setBackgroundResource(android.R.color.white); 1200 | mContent.setDrawingCacheEnabled(true); 1201 | mContent.measure(View.MeasureSpec.makeMeasureSpec((int) (mContent.getWidth() * 1), View.MeasureSpec.EXACTLY), 1202 | View.MeasureSpec.makeMeasureSpec((int) (mContent.getHeight() * 1), View.MeasureSpec.EXACTLY)); 1203 | mContent.layout(0, 0, mContent.getWidth(), mContent.getHeight()); 1204 | mContent.buildDrawingCache(); 1205 | 1206 | // 去掉状态栏 1207 | Bitmap cacheBmp = mContent.getDrawingCache(); 1208 | Bitmap resultBmp = null; 1209 | if (null != cacheBmp) { 1210 | int width = mContent.getWidth(); 1211 | int height = mContent.getHeight(); 1212 | resultBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 1213 | Canvas canvas = new Canvas(resultBmp); 1214 | Rect srcRect = new Rect(); 1215 | srcRect.left = 0; 1216 | srcRect.top = 0; 1217 | srcRect.right = cacheBmp.getWidth(); 1218 | srcRect.bottom = cacheBmp.getHeight(); 1219 | 1220 | Rect dstRect = new Rect(); 1221 | dstRect.left = 0; 1222 | dstRect.top = 0; 1223 | dstRect.right = width; 1224 | dstRect.bottom = height; 1225 | canvas.drawBitmap(cacheBmp, srcRect, dstRect, new Paint()); 1226 | cacheBmp.recycle(); 1227 | } 1228 | 1229 | // 销毁缓存信息 1230 | mContent.setDrawingCacheEnabled(false); 1231 | mContent.destroyDrawingCache(); 1232 | return resultBmp; 1233 | } 1234 | 1235 | 1236 | private boolean inRange(Rect srcRect, Rect checkRect) { 1237 | return srcRect.contains(checkRect); 1238 | } 1239 | 1240 | private List getNeedMoveView(Rect srcRect, ImageView imageView) { 1241 | List list = new ArrayList(); 1242 | for (View view : mViewList) { 1243 | if (view == imageView) { 1244 | continue; 1245 | } 1246 | int[] location = new int[2]; 1247 | view.getLocationOnScreen(location); 1248 | int x = location[0]; 1249 | int y = location[1]; 1250 | Rect checkRect = new Rect(); 1251 | checkRect.left = x + 25; 1252 | checkRect.right = x + view.getWidth() - 25; 1253 | checkRect.top = y + 25; 1254 | checkRect.bottom = y + view.getHeight() - 25; 1255 | if (inRange(srcRect, checkRect)) { 1256 | list.add(view); 1257 | } 1258 | } 1259 | 1260 | return list; 1261 | } 1262 | 1263 | private void copyView(View view) { 1264 | 1265 | final ImageView imageView = new ImageView(MainActivity.this); 1266 | ViewInfo tag = (ViewInfo) view.getTag(); 1267 | 1268 | ViewInfo viewInfo = new ViewInfo(tag.id, tag.degree); 1269 | viewInfo.type = tag.type; 1270 | viewInfo.color = tag.color; 1271 | viewInfo.realId = ++mRealInfoId; 1272 | viewInfo.width = tag.width; 1273 | viewInfo.height = tag.height; 1274 | 1275 | imageView.setTag(viewInfo); 1276 | 1277 | imageView.setScaleType(ImageView.ScaleType.FIT_XY); 1278 | setImageResource(imageView, false); 1279 | int[] location = new int[2]; 1280 | view.getLocationOnScreen(location); 1281 | locationX = location[0]; 1282 | locationY = location[1]; 1283 | imageView.setX(locationX + 5); 1284 | imageView.setY(locationY + 5); 1285 | imageView.setOnTouchListener(new MyTouchListener(imageView)); 1286 | if (viewInfo.degree != 0) { 1287 | imageView.setRotation(viewInfo.degree); 1288 | } 1289 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(view.getWidth(), view.getHeight()); 1290 | mContent.addView(imageView, params); 1291 | mViewList.add(imageView); 1292 | 1293 | createMemento(imageView, false, true); 1294 | } 1295 | 1296 | public class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener { 1297 | @Override 1298 | public boolean onScale(ScaleGestureDetector detector) { 1299 | float dSpan = detector.getCurrentSpan() - detector.getPreviousSpan(); 1300 | float dScale = (dSpan * 0.1f) / 100f; 1301 | float scale = mCurrentScale + dScale; 1302 | if (scale > 4.0f) { 1303 | scale = 4.0f; 1304 | } else if (scale < 0.5f) { 1305 | scale = 0.5f; 1306 | } 1307 | mContent.setScaleX(scale); 1308 | mContent.setScaleY(scale); 1309 | mCurrentScale = scale; 1310 | return true; 1311 | } 1312 | 1313 | @Override 1314 | public boolean onScaleBegin(ScaleGestureDetector detector) { 1315 | mMoved = false; 1316 | return true; 1317 | } 1318 | 1319 | @Override 1320 | public void onScaleEnd(ScaleGestureDetector detector) { 1321 | 1322 | } 1323 | } 1324 | } 1325 | -------------------------------------------------------------------------------- /app/src/main/java/com/lee/drawlayoutsample/MementoCreataker.java: -------------------------------------------------------------------------------- 1 | package com.lee.drawlayoutsample; 2 | 3 | import android.widget.Toast; 4 | 5 | import com.lee.drawlayoutsample.utils.BaseUtils; 6 | import com.lee.drawlayoutsample.utils.LogUtils; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by Lee on 2017/1/18. 13 | */ 14 | 15 | public class MementoCreataker { 16 | 17 | // private MementoOriginator mOriginator; 18 | private List mMementos = new ArrayList<>(); 19 | private List mBackups = new ArrayList<>(); 20 | 21 | public void createMemento(ElementMemento memento) { 22 | // ElementMemento memento = mOriginator.createMemento(); 23 | mMementos.add(memento); 24 | } 25 | 26 | public void createMemento(ElementMemento memento, int index) { 27 | 28 | int lastIndex = mMementos.size() - 1; 29 | LogUtils.i("lastIndex: " + lastIndex); 30 | LogUtils.d("start createMemento ,mMementos size:" + mMementos.size()+"\n #####"+mMementos.toString()); 31 | if (index > lastIndex) {//直接加在最后一个节点位置 32 | LogUtils.d("插入到最后一个位置:" + index); 33 | createMemento(memento); 34 | } else if (index >= 0 && index <= lastIndex) { 35 | LogUtils.d("插入到指定位置:" + index); 36 | for (int i = mMementos.size() - 1; i >= index; i--) { 37 | mMementos.remove(i); 38 | } 39 | createMemento(memento); 40 | } else { 41 | throw new IndexOutOfBoundsException(); 42 | } 43 | LogUtils.i("end createMemento ,mMementos size:" + mMementos.size()+"\n #####"+mMementos.toString()); 44 | } 45 | 46 | public ElementMemento restoreMemento(int index) { 47 | if (index < 0 || index >= mMementos.size()) { 48 | if (index < 0) { 49 | Toast.makeText(BaseUtils.getContext(), "没有上一步了", Toast.LENGTH_SHORT).show(); 50 | } else { 51 | Toast.makeText(BaseUtils.getContext(), "没有下一步了", Toast.LENGTH_SHORT).show(); 52 | } 53 | return null; 54 | } 55 | // ElementMemento elementMemento = mMementos.get(index); 56 | // mOriginator.restoreMemento(elementMemento); 57 | return mMementos.get(index); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/lee/drawlayoutsample/MementoOriginator.java: -------------------------------------------------------------------------------- 1 | package com.lee.drawlayoutsample; 2 | 3 | 4 | import com.lee.drawlayoutsample.utils.LogUtils; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by Lee on 2017/1/18. 11 | */ 12 | 13 | public class MementoOriginator { 14 | 15 | private List mInfos; 16 | 17 | public MementoOriginator() { 18 | mInfos = new ArrayList<>(); 19 | } 20 | 21 | public List restoreMemento(ElementMemento memento) { 22 | mInfos = memento.getInfos(); 23 | return mInfos; 24 | } 25 | 26 | public ElementMemento createMemento() { 27 | return new ElementMemento(mInfos); 28 | } 29 | 30 | public void setInfos(List infos) { 31 | mInfos = infos; 32 | } 33 | 34 | public void printMementos() { 35 | LogUtils.d("mInfos size: " + mInfos.size() + " $$$ mInfos: " + mInfos.toString()); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/lee/drawlayoutsample/ViewInfo.java: -------------------------------------------------------------------------------- 1 | package com.lee.drawlayoutsample; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * Created by Lee on 2016/11/4. 10 | */ 11 | 12 | public class ViewInfo implements Serializable, Parcelable { 13 | 14 | public ViewInfo(int id, float degree) { 15 | this.id = id; 16 | this.degree = degree; 17 | } 18 | 19 | 20 | public static final int TYPE_IMAGEVIEW = 0; 21 | public static final int TYPE_EDITTEXT = 1; 22 | 23 | public int id; 24 | public float degree; 25 | public int width; 26 | public int height; 27 | public float x; 28 | public float y; 29 | //0 imageView 1 EditText 30 | public int type; 31 | public int color; 32 | public int realId; 33 | 34 | // @Override 35 | // public boolean equals(Object o) { 36 | // if (o == null) { 37 | // return false; 38 | // } 39 | // if (o instanceof ViewInfo) { 40 | // ViewInfo info = (ViewInfo) o; 41 | // if (info.id == id && info.degree == degree && info.width == width && info.height == height 42 | // && info.x == x && info.y == y && info.type == type && info.color == color) { 43 | // return true; 44 | // } 45 | // } 46 | // return false; 47 | // } 48 | 49 | // @Override 50 | // public int hashCode() { 51 | // return Objects.hash(id, degree, width, height, x, y, type, color); 52 | // } 53 | 54 | public static final Creator CREATOR = new Creator() { 55 | @Override 56 | public ViewInfo createFromParcel(Parcel in) { 57 | return new ViewInfo(in); 58 | } 59 | 60 | @Override 61 | public ViewInfo[] newArray(int size) { 62 | return new ViewInfo[size]; 63 | } 64 | }; 65 | 66 | @Override 67 | public String toString() { 68 | return "ViewInfo{" + 69 | "id=" + id + 70 | ", degree=" + degree + 71 | ", width=" + width + 72 | ", height=" + height + 73 | ", x=" + x + 74 | ", y=" + y + 75 | ", type=" + type + 76 | ", color=" + color + 77 | ", realId=" + realId + 78 | '}'; 79 | } 80 | 81 | @Override 82 | public int describeContents() { 83 | return 0; 84 | } 85 | 86 | @Override 87 | public void writeToParcel(Parcel dest, int flags) { 88 | dest.writeInt(this.id); 89 | dest.writeFloat(this.degree); 90 | dest.writeInt(this.width); 91 | dest.writeInt(this.height); 92 | dest.writeFloat(this.x); 93 | dest.writeFloat(this.y); 94 | dest.writeInt(this.type); 95 | dest.writeInt(this.color); 96 | } 97 | 98 | protected ViewInfo(Parcel in) { 99 | this.id = in.readInt(); 100 | this.degree = in.readFloat(); 101 | this.width = in.readInt(); 102 | this.height = in.readInt(); 103 | this.x = in.readFloat(); 104 | this.y = in.readFloat(); 105 | this.type = in.readInt(); 106 | this.color = in.readInt(); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/lee/drawlayoutsample/utils/BaseUtils.java: -------------------------------------------------------------------------------- 1 | package com.lee.drawlayoutsample.utils; 2 | 3 | import android.content.Context; 4 | import android.content.res.AssetManager; 5 | import android.content.res.Configuration; 6 | import android.content.res.Resources; 7 | import android.support.annotation.NonNull; 8 | import android.util.DisplayMetrics; 9 | 10 | public class BaseUtils { 11 | 12 | private static Context context; 13 | 14 | public static void initialize(@NonNull Context context) { 15 | BaseUtils.context = context; 16 | } 17 | 18 | public static Context getContext() { 19 | synchronized (BaseUtils.class) { 20 | if (BaseUtils.context == null) 21 | throw new NullPointerException("Call BaseUtils.initialize(context) within your Application onCreate() method."); 22 | 23 | return BaseUtils.context.getApplicationContext(); 24 | } 25 | } 26 | 27 | public static Resources getResources() { 28 | return BaseUtils.getContext().getResources(); 29 | } 30 | 31 | public static Resources.Theme getTheme() { 32 | return BaseUtils.getContext().getTheme(); 33 | } 34 | 35 | public static AssetManager getAssets() { 36 | return BaseUtils.getContext().getAssets(); 37 | } 38 | 39 | public static Configuration getConfiguration() { 40 | return BaseUtils.getResources().getConfiguration(); 41 | } 42 | 43 | public static DisplayMetrics getDisplayMetrics() { 44 | return BaseUtils.getResources().getDisplayMetrics(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/lee/drawlayoutsample/utils/LogUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013. wyouflf (wyouflf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.lee.drawlayoutsample.utils; 17 | 18 | import android.text.TextUtils; 19 | import android.util.Log; 20 | 21 | /** 22 | * Log工具,类似android.util.Log。 23 | * tag自动产生,格式: customTagPrefix:className.methodName(L:lineNumber), 24 | * customTagPrefix为空时只输出:className.methodName(L:lineNumber)。 25 | */ 26 | public class LogUtils { 27 | 28 | public static String customTagPrefix = ""; 29 | 30 | private LogUtils() { 31 | } 32 | 33 | private static boolean allowLog = true; 34 | 35 | private static boolean allowD = true; 36 | 37 | private static boolean allowE = true; 38 | 39 | private static boolean allowI = true; 40 | 41 | private static boolean allowV = true; 42 | 43 | private static boolean allowW = true; 44 | 45 | private static boolean allowWtf = true; 46 | 47 | private static String generateTag(StackTraceElement caller) { 48 | String tag = "%s.%s(L:%d)"; 49 | String callerClazzName = caller.getClassName(); 50 | callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1); 51 | tag = String.format(tag, callerClazzName, caller.getMethodName(), caller.getLineNumber()); 52 | tag = TextUtils.isEmpty(customTagPrefix) ? tag : customTagPrefix + ":" + tag; 53 | return tag; 54 | } 55 | 56 | public static CustomLogger customLogger; 57 | 58 | public interface CustomLogger { 59 | void d(String tag, String content); 60 | 61 | void d(String tag, String content, Throwable tr); 62 | 63 | void e(String tag, String content); 64 | 65 | void e(String tag, String content, Throwable tr); 66 | 67 | void i(String tag, String content); 68 | 69 | void i(String tag, String content, Throwable tr); 70 | 71 | void v(String tag, String content); 72 | 73 | void v(String tag, String content, Throwable tr); 74 | 75 | void w(String tag, String content); 76 | 77 | void w(String tag, String content, Throwable tr); 78 | 79 | void w(String tag, Throwable tr); 80 | 81 | void wtf(String tag, String content); 82 | 83 | void wtf(String tag, String content, Throwable tr); 84 | 85 | void wtf(String tag, Throwable tr); 86 | } 87 | 88 | public static void d(String content) { 89 | if (!allowLog || !allowD) 90 | return; 91 | StackTraceElement caller = getCallerStackTraceElement(); 92 | String tag = generateTag(caller); 93 | 94 | if (customLogger != null) { 95 | customLogger.d(tag, "" + content); 96 | } else { 97 | Log.d(tag, "" + content); 98 | } 99 | } 100 | 101 | public static void d(String content, Throwable tr) { 102 | if (!allowLog || !allowD) 103 | return; 104 | StackTraceElement caller = getCallerStackTraceElement(); 105 | String tag = generateTag(caller); 106 | 107 | if (customLogger != null) { 108 | customLogger.d(tag, "" + content, tr); 109 | } else { 110 | Log.d(tag, "" + content, tr); 111 | } 112 | } 113 | 114 | public static void e(String content) { 115 | if (!allowE) 116 | return; 117 | StackTraceElement caller = getCallerStackTraceElement(); 118 | String tag = generateTag(caller); 119 | 120 | if (customLogger != null) { 121 | customLogger.e(tag, "" + content); 122 | } else { 123 | Log.e(tag, "" + content); 124 | } 125 | } 126 | 127 | public static void e(String content, Throwable tr) { 128 | if (!allowE) 129 | return; 130 | StackTraceElement caller = getCallerStackTraceElement(); 131 | String tag = generateTag(caller); 132 | 133 | if (customLogger != null) { 134 | customLogger.e(tag, "" + content, tr); 135 | } else { 136 | Log.e(tag, "" + content, tr); 137 | } 138 | } 139 | 140 | public static void i(String content) { 141 | if (!allowLog || !allowI) 142 | return; 143 | StackTraceElement caller = getCallerStackTraceElement(); 144 | String tag = generateTag(caller); 145 | 146 | if (customLogger != null) { 147 | customLogger.i(tag, "" + content); 148 | } else { 149 | Log.i(tag, "" + content); 150 | } 151 | } 152 | 153 | public static void i(String content, Throwable tr) { 154 | if (!allowLog || !allowI) 155 | return; 156 | StackTraceElement caller = getCallerStackTraceElement(); 157 | String tag = generateTag(caller); 158 | 159 | if (customLogger != null) { 160 | customLogger.i(tag, "" + content, tr); 161 | } else { 162 | Log.i(tag, "" + content, tr); 163 | } 164 | } 165 | 166 | public static void v(String content) { 167 | if (!allowLog || !allowV) 168 | return; 169 | StackTraceElement caller = getCallerStackTraceElement(); 170 | String tag = generateTag(caller); 171 | 172 | if (customLogger != null) { 173 | customLogger.v(tag, "" + content); 174 | } else { 175 | Log.v(tag, "" + content); 176 | } 177 | } 178 | 179 | public static void v(String content, Throwable tr) { 180 | if (!allowLog || !allowV) 181 | return; 182 | StackTraceElement caller = getCallerStackTraceElement(); 183 | String tag = generateTag(caller); 184 | 185 | if (customLogger != null) { 186 | customLogger.v(tag, "" + content, tr); 187 | } else { 188 | Log.v(tag, "" + content, tr); 189 | } 190 | } 191 | 192 | public static void w(String content) { 193 | if (!allowLog || !allowW) 194 | return; 195 | StackTraceElement caller = getCallerStackTraceElement(); 196 | String tag = generateTag(caller); 197 | 198 | if (customLogger != null) { 199 | customLogger.w(tag, "" + content); 200 | } else { 201 | Log.w(tag, "" + content); 202 | } 203 | } 204 | 205 | public static void w(String content, Throwable tr) { 206 | if (!allowLog || !allowW) 207 | return; 208 | StackTraceElement caller = getCallerStackTraceElement(); 209 | String tag = generateTag(caller); 210 | 211 | if (customLogger != null) { 212 | customLogger.w(tag, "" + content, tr); 213 | } else { 214 | Log.w(tag, "" + content, tr); 215 | } 216 | } 217 | 218 | public static void w(Throwable tr) { 219 | if (!allowLog || !allowW) 220 | return; 221 | StackTraceElement caller = getCallerStackTraceElement(); 222 | String tag = generateTag(caller); 223 | 224 | if (customLogger != null) { 225 | customLogger.w(tag, tr); 226 | } else { 227 | Log.w(tag, tr); 228 | } 229 | } 230 | 231 | 232 | public static void wtf(String content) { 233 | if (!allowLog || !allowWtf) 234 | return; 235 | StackTraceElement caller = getCallerStackTraceElement(); 236 | String tag = generateTag(caller); 237 | 238 | if (customLogger != null) { 239 | customLogger.wtf(tag, "" + content); 240 | } else { 241 | Log.wtf(tag, "" + content); 242 | } 243 | } 244 | 245 | public static void wtf(String content, Throwable tr) { 246 | if (!allowLog || !allowWtf) 247 | return; 248 | StackTraceElement caller = getCallerStackTraceElement(); 249 | String tag = generateTag(caller); 250 | 251 | if (customLogger != null) { 252 | customLogger.wtf(tag, "" + content, tr); 253 | } else { 254 | Log.wtf(tag, "" + content, tr); 255 | } 256 | } 257 | 258 | public static void wtf(Throwable tr) { 259 | if (!allowLog || !allowWtf) 260 | return; 261 | StackTraceElement caller = getCallerStackTraceElement(); 262 | String tag = generateTag(caller); 263 | 264 | if (customLogger != null) { 265 | customLogger.wtf(tag, tr); 266 | } else { 267 | Log.wtf(tag, tr); 268 | } 269 | } 270 | 271 | public static StackTraceElement getCallerStackTraceElement() { 272 | return Thread.currentThread().getStackTrace()[4]; 273 | } 274 | 275 | } 276 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/actionbar_ic_cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/actionbar_ic_cancel.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/actionbar_ic_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/actionbar_ic_copy.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/actionbar_ic_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/actionbar_ic_delete.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/actionbar_ic_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/actionbar_ic_done.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/all_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/all_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/all_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/all_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/back.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/forward.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/gallery_toolbar_chinabrush_press.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/gallery_toolbar_chinabrush_press.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/hot_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/hot_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/hot_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/hot_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu_rotate_light_n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/ic_menu_rotate_light_n.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/jewelry_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/jewelry_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/jewelry_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/jewelry_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/line.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/line_nomal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/line_nomal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/line_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/line_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/smile_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/smile_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/smile_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/smile_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/text_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/text_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/text_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/text_select.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/wangge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/drawable-hdpi/wangge.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_shape_blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_shape_focus.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_shape_green.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_shape_white.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/draw_selector_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_all_black.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_all_green.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_all_red.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_hot_black.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_hot_green.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_hot_red.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_jewelry_black.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_jewelry_green.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_jewelry_red.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_smile_black.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_smile_green.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_smile_red.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 33 | 34 | 39 | 40 | 48 | 49 | 58 | 67 | 76 | 77 | 78 | 79 | 81 | 82 | 83 | 93 | 94 | 104 | 105 | 115 | 116 | 126 | 127 | 136 | 137 | 146 | 157 | 167 | 177 | 178 | 182 | 183 | -------------------------------------------------------------------------------- /app/src/main/res/layout/draw_navigation_bar_layout.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 15 | 20 | 26 | 27 | 28 | 33 | 39 | 40 | 41 | 42 | 44 | 45 | 49 | 54 | 60 | 61 | 62 | 67 | 74 | 75 | 76 | 77 | 79 | 80 | 84 | 89 | 96 | 97 | 98 | 103 | 109 | 110 | 111 | 112 | 114 | 115 | 119 | 124 | 130 | 131 | 132 | 137 | 143 | 144 | 145 | 146 | 148 | 149 | 153 | 158 | 164 | 165 | 166 | 171 | 177 | 178 | 179 | 180 | 181 | 183 | 184 | 188 | 193 | 199 | 200 | 201 | 206 | 212 | 213 | 214 | 215 | 217 | 218 | 222 | 227 | 233 | 234 | 235 | 240 | 246 | 247 | 248 | 249 | 250 | 252 | 253 | 254 | 255 | 259 | 264 | 270 | 271 | 272 | 278 | 284 | 285 | 286 | 287 | 288 | 289 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | DrawLayoutSample 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/lee/drawlayoutsample/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.lee.drawlayoutsample; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnjoyAndroid/DrawLayoutSample/faa1906e097be400714047ea3b251c8cfbfe0b3e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jul 22 22:06:08 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------