├── .gitignore ├── .idea ├── .name ├── checkstyle-idea.xml ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── dictionaries │ └── angcyo.xml ├── encodings.xml ├── findbugs-idea.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── angcyo │ │ └── paintdemo │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ ├── com │ │ │ └── angcyo │ │ │ │ └── paintdemo │ │ │ │ ├── MainActivity.java │ │ │ │ ├── RUtil.java │ │ │ │ ├── paint │ │ │ │ ├── CircleShape.java │ │ │ │ ├── ImageShape.java │ │ │ │ ├── LineShape.java │ │ │ │ ├── PaintConfig.java │ │ │ │ ├── PaintShape.java │ │ │ │ ├── PaintView.java │ │ │ │ ├── PointShape.java │ │ │ │ └── RectShape.java │ │ │ │ └── socket │ │ │ │ ├── ClientSocket.java │ │ │ │ ├── ServerSocket.java │ │ │ │ └── SocketConfig.java │ │ ├── demo │ │ │ ├── Demo.java │ │ │ └── Demo2.java │ │ └── nt │ │ │ └── finger │ │ │ └── paint │ │ │ ├── DrawView.java │ │ │ ├── FileChooser.java │ │ │ ├── FingerPaint.java │ │ │ ├── FriendlyPoint.java │ │ │ └── Point.java │ └── res │ │ ├── anim │ │ ├── translate_in.xml │ │ └── translate_out.xml │ │ ├── drawable-hdpi │ │ └── icon.png │ │ ├── drawable-ldpi │ │ └── icon.png │ │ ├── drawable-mdpi │ │ └── icon.png │ │ ├── drawable │ │ ├── button_selector.xml │ │ └── popup_button_selector.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── content_main.xml │ │ ├── guide_client_layout.xml │ │ ├── guide_server_layout.xml │ │ ├── layout_col_popup.xml │ │ ├── layout_popup_tip.xml │ │ ├── layout_width_popup.xml │ │ └── main.xml │ │ ├── menu │ │ ├── menu_main.xml │ │ └── paint_menu.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── angcyo │ └── paintdemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── javademo ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── example │ └── MyClass.java └── 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 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | PaintDemo -------------------------------------------------------------------------------- /.idea/checkstyle-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 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/dictionaries/angcyo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/findbugs-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 32 | 203 | 216 | 225 | 226 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 24 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Android 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 67 | 68 | 70 | 71 | 72 | 73 | 74 | 75 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.angcyo.paintdemo" 9 | minSdkVersion 15 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 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 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.1.0' 26 | compile 'com.android.support:design:23.1.0' 27 | } 28 | -------------------------------------------------------------------------------- /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 D:\Android\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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/angcyo/paintdemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.graphics.Color; 8 | import android.graphics.drawable.ColorDrawable; 9 | import android.net.Uri; 10 | import android.os.Bundle; 11 | import android.support.design.widget.FloatingActionButton; 12 | import android.support.v4.content.LocalBroadcastManager; 13 | import android.support.v7.app.AppCompatActivity; 14 | import android.text.TextUtils; 15 | import android.view.Gravity; 16 | import android.view.LayoutInflater; 17 | import android.view.Menu; 18 | import android.view.MenuItem; 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | import android.widget.Button; 22 | import android.widget.EditText; 23 | import android.widget.PopupWindow; 24 | import android.widget.RadioGroup; 25 | import android.widget.TextView; 26 | import android.widget.Toast; 27 | 28 | import com.angcyo.paintdemo.paint.PaintConfig; 29 | import com.angcyo.paintdemo.paint.PaintView; 30 | import com.angcyo.paintdemo.socket.ServerSocket; 31 | import com.angcyo.paintdemo.socket.SocketConfig; 32 | 33 | import java.io.File; 34 | 35 | import nt.finger.paint.FingerPaint; 36 | 37 | public class MainActivity extends AppCompatActivity { 38 | 39 | public static final int GUIDE_WIN_OFFSET = 80; 40 | public static LocalBroadcastManager localBroadcastManager; 41 | boolean isShare = false; 42 | private BroadcastReceiver broadcastReceiver; 43 | private RadioGroup shapeGroup; 44 | private Button btUndo, btColor, btClear, btWidth, btCapture, btShare; 45 | private PaintView paintView; 46 | private PopupWindow mColPopup, mWidthPopup, mGuideServer, mGuideClient, mPopupTips; 47 | private View rootView; 48 | private String localIp; 49 | private ServerSocket mServerSocket; 50 | 51 | @Override 52 | protected void onCreate(Bundle savedInstanceState) { 53 | super.onCreate(savedInstanceState); 54 | // getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 55 | setContentView(R.layout.activity_main); 56 | FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 57 | fab.setOnClickListener(new View.OnClickListener() { 58 | @Override 59 | public void onClick(View view) { 60 | MainActivity.this.startActivity(new Intent(MainActivity.this, FingerPaint.class)); 61 | } 62 | }); 63 | 64 | fab.setVisibility(View.GONE); 65 | initView(); 66 | initEvent(); 67 | initData(); 68 | initBroadcast(); 69 | initShow(); 70 | } 71 | 72 | private void initShow() { 73 | rootView.postDelayed(new Runnable() { 74 | @Override 75 | public void run() { 76 | mGuideServer.showAtLocation(rootView, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, GUIDE_WIN_OFFSET); 77 | // showPopupTip("Hello Angcyo"); 78 | } 79 | }, 1000); 80 | } 81 | 82 | private void showPopupTip(String tip) { 83 | TextView tipView = ((TextView) mPopupTips.getContentView().findViewById(R.id.txTips)); 84 | tipView.setText(tip + ""); 85 | mPopupTips.showAtLocation(rootView, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, GUIDE_WIN_OFFSET); 86 | tipView.postDelayed(new Runnable() { 87 | @Override 88 | public void run() { 89 | if (mPopupTips != null && mPopupTips.isShowing()) { 90 | mPopupTips.dismiss(); 91 | } 92 | } 93 | }, 3000); 94 | } 95 | 96 | private void initBroadcast() { 97 | localBroadcastManager = LocalBroadcastManager.getInstance(this); 98 | IntentFilter intentFilter = new IntentFilter(PaintView.BDC_CAPTURE);//监听截图成功的广播 99 | intentFilter.addAction(SocketConfig.BDC_CONNECT_CLIENT); 100 | intentFilter.addAction(SocketConfig.BDC_CONNECT_SERVER); 101 | 102 | broadcastReceiver = new BroadcastReceiver() { 103 | @Override 104 | public void onReceive(Context context, Intent intent) { 105 | final Bundle bundle = intent.getExtras(); 106 | if (intent.getAction().equalsIgnoreCase(PaintView.BDC_CAPTURE)) {//截图广播 107 | if (bundle.getInt(PaintView.CAPTURE_CODE) == 1) { 108 | if (isShare) { 109 | share(bundle.getString(PaintView.CAPTURE_PATH)); 110 | isShare = false; 111 | } else { 112 | Toast.makeText(MainActivity.this, "保存至:" + bundle.getString(PaintView.CAPTURE_PATH), Toast.LENGTH_LONG).show(); 113 | } 114 | } else { 115 | Toast.makeText(MainActivity.this, "截图失败", Toast.LENGTH_LONG).show(); 116 | } 117 | } 118 | if (intent.getAction().equalsIgnoreCase(SocketConfig.BDC_CONNECT_CLIENT)) {//客户端连接广播 119 | runOnUiThread(new Runnable() { 120 | @Override 121 | public void run() { 122 | showPopupTip("客户端:" + bundle.getString(SocketConfig.KEY_CLIENT_IP) + "已连接"); 123 | } 124 | }); 125 | } 126 | if (intent.getAction().equalsIgnoreCase(SocketConfig.BDC_CONNECT_SERVER)) {//连上服务端广播 127 | runOnUiThread(new Runnable() { 128 | @Override 129 | public void run() { 130 | showPopupTip("已连上:" + bundle.getString(SocketConfig.KEY_SERVER_IP) + " 服务端"); 131 | } 132 | }); 133 | } 134 | } 135 | }; 136 | localBroadcastManager.registerReceiver(broadcastReceiver, intentFilter); 137 | } 138 | 139 | private void share(String file) { 140 | isShare = true; 141 | Intent intent = new Intent(Intent.ACTION_SEND); 142 | if (file != null) { 143 | intent.setType("image/*"); 144 | intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(file))); 145 | } else { 146 | intent.setType("text/plain"); 147 | } 148 | intent.putExtra(Intent.EXTRA_SUBJECT, "快快来围观"); 149 | intent.putExtra(Intent.EXTRA_TEXT, "为何可以这么美"); 150 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 151 | startActivity(Intent.createChooser(intent, "分享至")); 152 | } 153 | 154 | private void initData() { 155 | switch (getIndex((RadioGroup) mColPopup.getContentView().findViewById(R.id.colRG))) { 156 | case 1: 157 | PaintConfig.getInstance().setCurrentShapeColor(getResources().getColor(R.color.col2)); 158 | break; 159 | case 2: 160 | PaintConfig.getInstance().setCurrentShapeColor(getResources().getColor(R.color.col3)); 161 | break; 162 | case 3: 163 | PaintConfig.getInstance().setCurrentShapeColor(getResources().getColor(R.color.col4)); 164 | break; 165 | case 4: 166 | PaintConfig.getInstance().setCurrentShapeColor(getResources().getColor(R.color.col5)); 167 | break; 168 | case 0: 169 | default: 170 | PaintConfig.getInstance().setCurrentShapeColor(getResources().getColor(R.color.col1)); 171 | break; 172 | } 173 | switch (getIndex((RadioGroup) mWidthPopup.getContentView().findViewById(R.id.widthRG))) { 174 | case 1: 175 | PaintConfig.getInstance().setCurrentShapeWidth(getResources().getDimension(R.dimen.width2)); 176 | break; 177 | case 2: 178 | PaintConfig.getInstance().setCurrentShapeWidth(getResources().getDimension(R.dimen.width3)); 179 | break; 180 | case 3: 181 | PaintConfig.getInstance().setCurrentShapeWidth(getResources().getDimension(R.dimen.width4)); 182 | break; 183 | case 4: 184 | PaintConfig.getInstance().setCurrentShapeWidth(getResources().getDimension(R.dimen.width5)); 185 | break; 186 | case 0: 187 | default: 188 | PaintConfig.getInstance().setCurrentShapeWidth(getResources().getDimension(R.dimen.width1)); 189 | break; 190 | } 191 | 192 | localIp = RUtil.getIp(this); 193 | ((TextView) mGuideServer.getContentView().findViewById(R.id.tips)).setText("本机IP:" + localIp); 194 | } 195 | 196 | private void initEvent() { 197 | shapeGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { 198 | @Override 199 | public void onCheckedChanged(RadioGroup group, int checkedId) { 200 | switch (checkedId) { 201 | case R.id.line: 202 | PaintConfig.getInstance().setCurrentShape(PaintConfig.Shape.Line); 203 | break; 204 | case R.id.circle: 205 | PaintConfig.getInstance().setCurrentShape(PaintConfig.Shape.Circle); 206 | break; 207 | case R.id.rect: 208 | PaintConfig.getInstance().setCurrentShape(PaintConfig.Shape.Rect); 209 | break; 210 | case R.id.point: 211 | default: 212 | PaintConfig.getInstance().setCurrentShape(PaintConfig.Shape.Point); 213 | break; 214 | } 215 | } 216 | }); 217 | 218 | btUndo.setOnClickListener(new View.OnClickListener() { 219 | @Override 220 | public void onClick(View v) { 221 | paintView.removeLastOne(); 222 | } 223 | }); 224 | btClear.setOnClickListener(new View.OnClickListener() { 225 | @Override 226 | public void onClick(View v) { 227 | paintView.removeAll(); 228 | } 229 | }); 230 | btCapture.setOnClickListener(new View.OnClickListener() { 231 | @Override 232 | public void onClick(View v) { 233 | paintView.capture(); 234 | } 235 | }); 236 | btShare.setOnClickListener(new View.OnClickListener() { 237 | @Override 238 | public void onClick(View v) { 239 | paintView.capture(); 240 | isShare = true; 241 | } 242 | }); 243 | 244 | btColor.setOnClickListener(new View.OnClickListener() { 245 | @Override 246 | public void onClick(View v) { 247 | mColPopup.showAsDropDown(btColor, btColor.getWidth(), -btColor.getHeight() - 6); 248 | } 249 | }); 250 | 251 | btWidth.setOnClickListener(new View.OnClickListener() { 252 | @Override 253 | public void onClick(View v) { 254 | mWidthPopup.showAsDropDown(btWidth, btWidth.getWidth(), -btWidth.getHeight() - 6); 255 | } 256 | }); 257 | 258 | mColPopup.setOnDismissListener(new PopupWindow.OnDismissListener() { 259 | @Override 260 | public void onDismiss() { 261 | initData(); 262 | } 263 | }); 264 | mWidthPopup.setOnDismissListener(new PopupWindow.OnDismissListener() { 265 | @Override 266 | public void onDismiss() { 267 | initData(); 268 | } 269 | }); 270 | 271 | //作为服务端 272 | mGuideServer.getContentView().findViewById(R.id.btServer).setOnClickListener(new View.OnClickListener() { 273 | @Override 274 | public void onClick(View v) { 275 | mGuideServer.dismiss(); 276 | SocketConfig.isServer = true; 277 | SocketConfig.SVR_IP = localIp; 278 | mServerSocket = new ServerSocket(paintView); 279 | new Thread(mServerSocket).start(); 280 | showPopupTip("等待连接..."); 281 | } 282 | }); 283 | 284 | //作为客户端 285 | mGuideServer.getContentView().findViewById(R.id.btClient).setOnClickListener(new View.OnClickListener() { 286 | @Override 287 | public void onClick(View v) { 288 | mGuideServer.dismiss(); 289 | SocketConfig.isServer = false; 290 | mGuideClient.showAtLocation(rootView, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, GUIDE_WIN_OFFSET); 291 | } 292 | }); 293 | 294 | //客户端输入服务器IP 295 | final EditText etServer = (EditText) mGuideClient.getContentView().findViewById(R.id.etServer); 296 | mGuideClient.getContentView().findViewById(R.id.btSaveIp).setOnClickListener(new View.OnClickListener() { 297 | @Override 298 | public void onClick(View v) { 299 | String svrIp = etServer.getText().toString(); 300 | if (TextUtils.isEmpty(svrIp)) { 301 | etServer.setError("请输入有效值"); 302 | etServer.requestFocus(); 303 | return; 304 | } 305 | mGuideClient.dismiss(); 306 | SocketConfig.SVR_IP = svrIp; 307 | // SocketConfig.isStartClient = true; 308 | paintView.connectServer(); 309 | showPopupTip("正在连接...:" + svrIp); 310 | } 311 | }); 312 | } 313 | 314 | private void initView() { 315 | rootView = findViewById(R.id.rootView); 316 | shapeGroup = (RadioGroup) findViewById(R.id.shape_group); 317 | btUndo = (Button) findViewById(R.id.cancel); 318 | btClear = (Button) findViewById(R.id.clear); 319 | btColor = (Button) findViewById(R.id.color); 320 | btWidth = (Button) findViewById(R.id.width); 321 | btCapture = (Button) findViewById(R.id.capture); 322 | btShare = (Button) findViewById(R.id.share); 323 | paintView = (PaintView) findViewById(R.id.paint_view); 324 | 325 | //颜色选择弹窗 326 | LayoutInflater inflater = LayoutInflater.from(this); 327 | View popColLayout = inflater.inflate(R.layout.layout_col_popup, null); 328 | mColPopup = new PopupWindow(popColLayout, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true); 329 | mColPopup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));//不设置背景,无法响应 返回键 和 界面外点击事件;应该算是谷歌大神的BUG 330 | 331 | //粗细选择弹窗 332 | View popWidthLayout = inflater.inflate(R.layout.layout_width_popup, null); 333 | mWidthPopup = new PopupWindow(popWidthLayout, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true); 334 | mWidthPopup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 335 | 336 | //服务端向导 337 | View guideServer = inflater.inflate(R.layout.guide_server_layout, null); 338 | mGuideServer = new PopupWindow(guideServer, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true); 339 | mGuideServer.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 340 | mGuideServer.setAnimationStyle(R.style.GuideWindowAnimation); 341 | 342 | //客户端向导 343 | View guideClient = inflater.inflate(R.layout.guide_client_layout, null); 344 | mGuideClient = new PopupWindow(guideClient, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true); 345 | mGuideClient.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 346 | mGuideClient.setAnimationStyle(R.style.GuideWindowAnimation); 347 | 348 | //弹窗提示 349 | View popupTip = inflater.inflate(R.layout.layout_popup_tip, null); 350 | mPopupTips = new PopupWindow(popupTip, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true); 351 | mPopupTips.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 352 | mPopupTips.setAnimationStyle(R.style.GuideWindowAnimation); 353 | mPopupTips.setTouchable(false); 354 | mPopupTips.setOutsideTouchable(false); 355 | } 356 | 357 | private int getIndex(RadioGroup radioGroup) { 358 | return radioGroup.indexOfChild(radioGroup.findViewById(radioGroup.getCheckedRadioButtonId())); 359 | } 360 | 361 | private void setIndex(RadioGroup radioGroup, int index) { 362 | radioGroup.check(radioGroup.getChildAt(index).getId()); 363 | } 364 | 365 | 366 | @Override 367 | protected void onDestroy() { 368 | if (mServerSocket != null) { 369 | mServerSocket.exit(); 370 | } 371 | localBroadcastManager.unregisterReceiver(broadcastReceiver); 372 | localBroadcastManager = null; 373 | super.onDestroy(); 374 | } 375 | 376 | @Override 377 | public void onBackPressed() { 378 | android.os.Process.killProcess(android.os.Process.myPid()); 379 | super.onBackPressed(); 380 | } 381 | 382 | @Override 383 | public boolean onCreateOptionsMenu(Menu menu) { 384 | // Inflate the menu; this adds items to the action bar if it is present. 385 | getMenuInflater().inflate(R.menu.menu_main, menu); 386 | return true; 387 | } 388 | 389 | @Override 390 | public boolean onOptionsItemSelected(MenuItem item) { 391 | // Handle action bar item clicks here. The action bar will 392 | // automatically handle clicks on the Home/Up button, so long 393 | // as you specify a parent activity in AndroidManifest.xml. 394 | int id = item.getItemId(); 395 | 396 | //noinspection SimplifiableIfStatement 397 | if (id == R.id.action_settings) { 398 | return true; 399 | } 400 | 401 | return super.onOptionsItemSelected(item); 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/RUtil.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.PackageManager; 7 | import android.graphics.Point; 8 | import android.net.ConnectivityManager; 9 | import android.net.NetworkInfo; 10 | import android.net.Uri; 11 | import android.net.wifi.WifiInfo; 12 | import android.net.wifi.WifiManager; 13 | import android.os.Build; 14 | import android.os.Environment; 15 | import android.telephony.TelephonyManager; 16 | import android.text.TextUtils; 17 | import android.util.DisplayMetrics; 18 | import android.util.Log; 19 | import android.view.WindowManager; 20 | 21 | import java.io.File; 22 | import java.io.FileInputStream; 23 | import java.io.FileOutputStream; 24 | import java.io.InputStream; 25 | import java.io.OutputStream; 26 | import java.net.InetAddress; 27 | import java.net.NetworkInterface; 28 | import java.net.SocketException; 29 | import java.text.DecimalFormat; 30 | import java.text.SimpleDateFormat; 31 | import java.util.Date; 32 | import java.util.Enumeration; 33 | import java.util.Properties; 34 | import java.util.Random; 35 | 36 | /** 37 | * Created by angcyo on 15-09-08-008. 38 | */ 39 | public class RUtil { 40 | static String config = getSDPath() + File.separator + "config.ini"; 41 | 42 | /** 43 | * 去除字符串左右的字符 44 | */ 45 | public static String trimMarks(String des) { 46 | return trimMarks(des, 1); 47 | } 48 | 49 | /** 50 | * 去除字符串左右指定个数的字符 51 | */ 52 | public static String trimMarks(String des, int count) { 53 | if (des == null || count < 0 || des.length() < count + 1) { 54 | return des; 55 | } 56 | return des.substring(count, des.length() - count); 57 | } 58 | 59 | /** 60 | * 返回现在的时间,不包含日期 61 | */ 62 | public static String getNowTime() { 63 | return getNowTime("HH:mm:ss"); 64 | } 65 | 66 | public static String getNowTime(String pattern) { 67 | SimpleDateFormat dateFormat = new SimpleDateFormat(pattern); 68 | return dateFormat.format(new Date()); 69 | } 70 | 71 | /** 72 | * 判断字符串是否为空 73 | */ 74 | public static boolean isEmpty(String str) { 75 | return (str == null || str.trim().length() < 1); 76 | } 77 | 78 | /** 79 | * 判断网络是否可以用 80 | * 81 | * @param context the con 82 | * @return the boolean 83 | */ 84 | public static boolean isNetOk(Context context) { 85 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 86 | if (cm == null) 87 | return false; 88 | NetworkInfo netInfo = cm.getActiveNetworkInfo(); 89 | if (netInfo == null) { 90 | return false; 91 | } 92 | if (netInfo.isConnected()) { 93 | return true; 94 | } 95 | return false; 96 | } 97 | 98 | // 可以使用Time类,更简单 99 | 100 | /** 101 | * 获取网络的名字 102 | * 103 | * @param context the context 104 | * @return the net type name 105 | */ 106 | public static String getNetTypeName(Context context) { 107 | ConnectivityManager connectivityManager = (ConnectivityManager) context 108 | .getSystemService(Context.CONNECTIVITY_SERVICE); 109 | if (connectivityManager != null) { 110 | NetworkInfo activeNetworkInfo = connectivityManager 111 | .getActiveNetworkInfo(); 112 | if (activeNetworkInfo == null) { 113 | return "无网络"; 114 | } 115 | if (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) { 116 | return activeNetworkInfo.getTypeName(); 117 | } else { 118 | String typeName = activeNetworkInfo.getSubtypeName(); 119 | // MyLog.i("网络名称:", typeName); 120 | if (typeName == null || typeName.length() == 0) { 121 | return "未知网络"; 122 | } else if (typeName.length() > 3) { 123 | return activeNetworkInfo.getSubtypeName().substring(0, 4); 124 | } else { 125 | return activeNetworkInfo.getSubtypeName().substring(0, 126 | typeName.length()); 127 | } 128 | } 129 | } else { 130 | return "无网络"; 131 | } 132 | } 133 | 134 | /** 135 | * @return 按照HH:mm:ss 返回时间 136 | */ 137 | public static String getTime() { 138 | SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); 139 | return format.format(new Date()); 140 | } 141 | 142 | /** 143 | * @return 按照yyyy-MM-dd 格式返回日期 144 | */ 145 | public static String getDate() { 146 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); 147 | return format.format(new Date()); 148 | } 149 | 150 | /** 151 | * @return 按照yyyy-MM-dd HH:mm:ss 的格式返回日期和时间 152 | */ 153 | public static String getDateAndTime() { 154 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 155 | return format.format(new Date()); 156 | } 157 | 158 | /** 159 | * @param pattern 格式 160 | * @return 返回日期 161 | */ 162 | public static String getDate(String pattern) { 163 | SimpleDateFormat format = new SimpleDateFormat(pattern); 164 | return format.format(new Date()); 165 | } 166 | 167 | /** 168 | * @param pattern 格式 169 | * @return 按照指定格式返回时间 170 | */ 171 | public static String getTime(String pattern) { 172 | SimpleDateFormat format = new SimpleDateFormat(pattern); 173 | return format.format(new Date()); 174 | } 175 | 176 | /** 177 | * @param pattern 指定的格式 178 | * @return 按照指定格式返回日期和时间 179 | */ 180 | public static String getDateAndTime(String pattern) { 181 | SimpleDateFormat format = new SimpleDateFormat(pattern); 182 | return format.format(new Date()); 183 | } 184 | 185 | // 186 | 187 | /** 188 | * 需要权限 189 | * 190 | * @param context 上下文 191 | * @return 返回手机号码 192 | */ 193 | public static String getTelNumber(Context context) { 194 | TelephonyManager tm = (TelephonyManager) context 195 | .getSystemService(Context.TELEPHONY_SERVICE); 196 | return tm.getLine1Number(); 197 | } 198 | 199 | /** 200 | * 取得device的IP address 201 | *

202 | * 需要权限 android.permission.ACCESS_WIFI_STATE 203 | * 204 | * @param context 205 | * @return 返回ip 206 | */ 207 | public static String getWifiIp(Context context) { 208 | WifiManager wifiManager = (WifiManager) context 209 | .getSystemService(Context.WIFI_SERVICE); 210 | WifiInfo wifiInfo = wifiManager.getConnectionInfo(); 211 | int ipAddress = wifiInfo.getIpAddress(); 212 | 213 | // 格式化IP address,例如:格式化前:1828825280,格式化后:192.168.1.109 214 | String ip = String.format("%d.%d.%d.%d", (ipAddress & 0xff), 215 | (ipAddress >> 8 & 0xff), (ipAddress >> 16 & 0xff), 216 | (ipAddress >> 24 & 0xff)); 217 | return ip; 218 | } 219 | 220 | /** 221 | * 获取网络ip 222 | */ 223 | public static String getIp(Context context) { 224 | if (isWifiConnected(context)) { 225 | return getWifiIp(context); 226 | } else { 227 | return getMobileIP(); 228 | } 229 | } 230 | 231 | /** 232 | * Get IP For mobile 233 | */ 234 | public static String getMobileIP() { 235 | try { 236 | for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) { 237 | NetworkInterface intf = (NetworkInterface) en.nextElement(); 238 | for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) { 239 | InetAddress inetAddress = enumIpAddr.nextElement(); 240 | if (!inetAddress.isLoopbackAddress()) { 241 | String ipaddress = inetAddress.getHostAddress().toString(); 242 | return ipaddress; 243 | } 244 | } 245 | } 246 | } catch (SocketException ex) { 247 | Log.e("getMobileIP", "Exception in Get IP Address: " + ex.toString()); 248 | } 249 | return ""; 250 | } 251 | 252 | /** 253 | * @return 获取device的os version 254 | */ 255 | public static String getOsVersion() { 256 | String string = Build.VERSION.RELEASE; 257 | return string; 258 | } 259 | 260 | /** 261 | * @return 返回设备型号 262 | */ 263 | public static String getDeviceName() { 264 | String string = Build.MODEL; 265 | return string; 266 | } 267 | 268 | /** 269 | * INTERNAL method that returns the device model name with correct capitalization. 270 | * Taken from: http://stackoverflow.com/a/12707479/1254846 271 | * 272 | * @return The device model name (i.e., "LGE Nexus 5") 273 | */ 274 | public static String getDeviceModelName() { 275 | String manufacturer = Build.MANUFACTURER; 276 | String model = Build.MODEL; 277 | if (model.startsWith(manufacturer)) { 278 | return capitalize(model); 279 | } else { 280 | return capitalize(manufacturer) + " " + model; 281 | } 282 | } 283 | 284 | /** 285 | * INTERNAL method that capitalizes the first character of a string 286 | * 287 | * @param s The string to capitalize 288 | * @return The capitalized string 289 | */ 290 | public static String capitalize(String s) { 291 | if (s == null || s.length() == 0) { 292 | return ""; 293 | } 294 | char first = s.charAt(0); 295 | if (Character.isUpperCase(first)) { 296 | return s; 297 | } else { 298 | return Character.toUpperCase(first) + s.substring(1); 299 | } 300 | } 301 | 302 | /** 303 | * @return 返回设备sdk版本 304 | */ 305 | public static String getOsSdk() { 306 | int sdk = Build.VERSION.SDK_INT; 307 | return String.valueOf(sdk); 308 | } 309 | 310 | public static int getRandom() { 311 | Random random = new Random(); 312 | return random.nextInt(); 313 | } 314 | 315 | /** 316 | * 获取随机数 317 | * 318 | * @param n 最大范围 319 | * @return 320 | */ 321 | public static int getRandom(int n) { 322 | Random random = new Random(); 323 | return random.nextInt(n); 324 | } 325 | 326 | /** 327 | * 获取字符数组中随机的字符串 328 | * 329 | * @param context 330 | * @param resId 331 | * @return 332 | */ 333 | public static String getRandomString(Context context, int resId) { 334 | String[] strings; 335 | strings = context.getResources().getStringArray(resId); 336 | 337 | return strings[getRandom(strings.length)]; 338 | } 339 | 340 | /** 341 | * 从资源id获取字符串 342 | * 343 | * @param context 上下文 344 | * @param id 资源id 345 | * @return 字符串 346 | */ 347 | public static String getStringForRes(Context context, int id) { 348 | if (context == null) { 349 | return ""; 350 | } 351 | return context.getResources().getString(id); 352 | } 353 | 354 | /** 355 | * 返回app的版本名称. 356 | * 357 | * @param context 358 | * @return 359 | */ 360 | public static String getAppVersionName(Context context) { 361 | String version = "unknown"; 362 | // 获取package manager的实例 363 | PackageManager packageManager = context.getPackageManager(); 364 | // getPackageName()是你当前类的包名,0代表是获取版本信息 365 | PackageInfo packInfo; 366 | try { 367 | packInfo = packageManager.getPackageInfo(context.getPackageName(), 368 | 0); 369 | version = packInfo.versionName; 370 | } catch (PackageManager.NameNotFoundException e) { 371 | e.printStackTrace(); 372 | } 373 | // Log.i("版本名称:", version); 374 | return version; 375 | } 376 | 377 | /** 378 | * 返回app的版本代码. 379 | * 380 | * @param context 381 | * @return 382 | */ 383 | public static int getAppVersionCode(Context context) { 384 | // 获取package manager的实例 385 | PackageManager packageManager = context.getPackageManager(); 386 | // getPackageName()是你当前类的包名,0代表是获取版本信息 387 | int code = 1; 388 | PackageInfo packInfo; 389 | try { 390 | packInfo = packageManager.getPackageInfo(context.getPackageName(), 391 | 0); 392 | code = packInfo.versionCode; 393 | } catch (PackageManager.NameNotFoundException e) { 394 | e.printStackTrace(); 395 | } 396 | // Log.i("版本代码:", version); 397 | return code; 398 | } 399 | 400 | /** 401 | * 判断网络是否可用 402 | * 403 | * @param context 404 | * @return 405 | */ 406 | public static boolean isNetworkOK(Context context) { 407 | ConnectivityManager cm = (ConnectivityManager) context 408 | .getSystemService(Context.CONNECTIVITY_SERVICE); 409 | 410 | NetworkInfo info = cm.getActiveNetworkInfo(); 411 | 412 | if (info != null && info.isConnectedOrConnecting()) { 413 | return true; 414 | } 415 | return false; 416 | } 417 | 418 | /** 419 | * 判断wifi是否可用 420 | * 421 | * @param context 422 | * @return 423 | */ 424 | public static boolean isWifiConnected(Context context) { 425 | if (context != null) { 426 | ConnectivityManager mConnectivityManager = (ConnectivityManager) context 427 | .getSystemService(Context.CONNECTIVITY_SERVICE); 428 | NetworkInfo mWiFiNetworkInfo = mConnectivityManager 429 | .getNetworkInfo(ConnectivityManager.TYPE_WIFI); // 返回WIFI 430 | // 的连接info 431 | if (mWiFiNetworkInfo != null) { 432 | return mWiFiNetworkInfo.isAvailable(); 433 | } 434 | } 435 | return false; 436 | } 437 | 438 | /** 439 | * 获取屏幕的宽度高度 440 | * 441 | * @param context 442 | * @param size 443 | */ 444 | public static DisplayMetrics getDisplay(Context context, Point size) { 445 | WindowManager windowManager = (WindowManager) context 446 | .getSystemService(Context.WINDOW_SERVICE); 447 | windowManager.getDefaultDisplay().getSize(size); 448 | DisplayMetrics outMetrics = new DisplayMetrics(); 449 | windowManager.getDefaultDisplay().getMetrics(outMetrics); 450 | return outMetrics; 451 | } 452 | 453 | /** 454 | * 格式化字节 455 | * 456 | * @param size 457 | * @return 458 | * @date 2014年12月3日 459 | */ 460 | public static String formatSize(long size) { 461 | if (size / (1024 * 1024) > 0) { 462 | float tmpSize = (float) (size) / (float) (1024 * 1024); 463 | DecimalFormat df = new DecimalFormat("#.##"); 464 | return "" + df.format(tmpSize) + "MB"; 465 | } else if (size / 1024 > 0) { 466 | return "" + (size / (1024)) + "KB"; 467 | } else 468 | return "" + size + "B"; 469 | } 470 | 471 | public static String formatSize(String size) { 472 | 473 | if (size.substring(size.length() - 1).equalsIgnoreCase("B") 474 | || size.endsWith("-")) { 475 | return size; 476 | } else { 477 | return formatSize(Long.valueOf(size)); 478 | } 479 | 480 | } 481 | 482 | /** 483 | * 是否分割kb单位 484 | * 485 | * @param size 486 | * @param isNewLine 487 | * @return 488 | * @date 2014年12月3日 489 | */ 490 | public static String formatSize(long size, boolean isNewLine) { 491 | if (isNewLine == false) { 492 | return formatSize(size); 493 | } 494 | if (size / (1024 * 1024) > 0) { 495 | float tmpSize = (float) (size) / (float) (1024 * 1024); 496 | DecimalFormat df = new DecimalFormat("#.##"); 497 | return "" + df.format(tmpSize) + "\nMB"; 498 | } else if (size / 1024 > 0) { 499 | return "" + (size / (1024)) + "\nKB"; 500 | } else 501 | return "" + size + "\nB"; 502 | } 503 | 504 | public static String callMethodAndLine() { 505 | StringBuilder result = new StringBuilder(); 506 | StackTraceElement thisMethodStack = (new Exception()).getStackTrace()[1]; 507 | result.append(thisMethodStack.getClassName() + "."); 508 | result.append(thisMethodStack.getMethodName()); 509 | result.append("(" + thisMethodStack.getFileName()); 510 | result.append(":" + thisMethodStack.getLineNumber() + ")"); 511 | return result.toString(); 512 | } 513 | 514 | public static String callClassName() { 515 | StringBuilder result = new StringBuilder(); 516 | StackTraceElement thisMethodStack = (new Exception()).getStackTrace()[1]; 517 | result.append(thisMethodStack.getClassName()); 518 | return result.toString(); 519 | } 520 | 521 | public static String callMethodName() { 522 | StringBuilder result = new StringBuilder(); 523 | StackTraceElement thisMethodStack = (new Exception()).getStackTrace()[1]; 524 | result.append(thisMethodStack.getMethodName()); 525 | return result.toString(); 526 | } 527 | 528 | public static String callClassMethodName() { 529 | StringBuilder result = new StringBuilder(); 530 | StackTraceElement thisMethodStack = (new Exception()).getStackTrace()[1]; 531 | result.append(thisMethodStack.getClassName() + "."); 532 | result.append(thisMethodStack.getMethodName()); 533 | return result.toString(); 534 | } 535 | 536 | /** 537 | * 返回SD卡路径,如果没有返回默认的下载缓存路径 538 | * 539 | * @return the sD path 540 | */ 541 | public static String getSDPath() { 542 | return isExternalStorageWritable() ? Environment 543 | .getExternalStorageDirectory().getPath() : Environment 544 | .getDownloadCacheDirectory().getPath(); 545 | } 546 | 547 | /** 548 | * 判断是否有SD卡 549 | * 550 | * @return the boolean 551 | */ 552 | public static boolean isExternalStorageWritable() { 553 | String state = Environment.getExternalStorageState(); 554 | if (Environment.MEDIA_MOUNTED.equals(state)) { 555 | return true; 556 | } 557 | return false; 558 | } 559 | 560 | /** 561 | * 从配置文件中,获取值 562 | * 563 | * @param key the key 564 | * @param config the config 565 | * @return the string 566 | */ 567 | public static String get(String key, String config) { 568 | FileInputStream s; 569 | String value = ""; 570 | try { 571 | s = new FileInputStream(config); 572 | Properties properties = new Properties(); 573 | properties.load(s); 574 | value = properties.getProperty(key, ""); 575 | s.close(); 576 | } catch (Exception e) { 577 | } 578 | return value; 579 | } 580 | 581 | /** 582 | * 保存到配置文件 格式: key!value 583 | * 584 | * @param content the content 585 | * @param config the config 586 | */ 587 | public static void set(String content, String config) { 588 | Properties prop = new Properties(); 589 | String[] values; 590 | InputStream fis = null; 591 | OutputStream fos = null; 592 | try { 593 | fis = new FileInputStream(config); 594 | fos = new FileOutputStream(config); 595 | prop.load(fis); 596 | values = content.split("!"); 597 | String value = values.length == 1 ? "" : values[1].trim(); 598 | prop.setProperty(values[0].trim(), value); 599 | prop.storeToXML(fos, " by angcyo", "utf-8"); 600 | } catch (Exception e) { 601 | } finally { 602 | try { 603 | fis.close(); 604 | fos.close(); 605 | } catch (Exception e2) { 606 | } 607 | } 608 | } 609 | 610 | public static String get(String key) { 611 | return get(key, config); 612 | } 613 | 614 | public static void set(String content) { 615 | set(content, config); 616 | } 617 | 618 | /** 619 | * 打开网页 620 | * 621 | * @param context the context 622 | * @param url the url 623 | */ 624 | public static void openUrl(Context context, String url) { 625 | if (TextUtils.isEmpty(url)) { 626 | return; 627 | } 628 | 629 | if (!url.toLowerCase().startsWith("http:") || !url.toLowerCase().startsWith("https:")) { 630 | url = "http:".concat(url); 631 | } 632 | 633 | Uri webPage = Uri.parse(url); 634 | Intent webIntent = new Intent(Intent.ACTION_VIEW, webPage); 635 | context.startActivity(webIntent); 636 | } 637 | 638 | /** 639 | * 检查是否有网络 640 | */ 641 | public static boolean isNetworkAvailable(Context context) { 642 | NetworkInfo info = getNetworkInfo(context); 643 | if (info != null) { 644 | return info.isAvailable(); 645 | } 646 | return false; 647 | } 648 | 649 | /** 650 | * 检查是否是WIFI 651 | */ 652 | public static boolean isWifi(Context context) { 653 | NetworkInfo info = getNetworkInfo(context); 654 | if (info != null) { 655 | if (info.getType() == ConnectivityManager.TYPE_WIFI) 656 | return true; 657 | } 658 | return false; 659 | } 660 | 661 | /** 662 | * 检查是否是移动网络 663 | */ 664 | public static boolean isMobile(Context context) { 665 | NetworkInfo info = getNetworkInfo(context); 666 | if (info != null) { 667 | if (info.getType() == ConnectivityManager.TYPE_MOBILE) 668 | return true; 669 | } 670 | return false; 671 | } 672 | 673 | private static NetworkInfo getNetworkInfo(Context context) { 674 | 675 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 676 | return cm.getActiveNetworkInfo(); 677 | } 678 | } 679 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/paint/CircleShape.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo.paint; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | 6 | /** 7 | * Created by angcyo on 15-10-29-029. 8 | */ 9 | public class CircleShape extends PaintShape { 10 | 11 | 12 | public CircleShape(float mStartX, float mStartY, Paint mPaint) { 13 | super(mStartX, mStartY, mPaint); 14 | } 15 | 16 | public CircleShape(Paint paint) { 17 | super(paint); 18 | } 19 | 20 | @Override 21 | public void onDraw(Canvas canvas) { 22 | float a = mEndX - mStartX; 23 | float b = mEndY - mStartY; 24 | float c = (float) Math.sqrt(Math.pow(Math.abs(a), 2) + Math.pow(Math.abs(b), 2)); 25 | 26 | float centreX = Math.abs((mEndX - mStartX)) / 2 + Math.min(mStartX, mEndX); 27 | float centreY = Math.abs((mEndY - mStartY)) / 2 + Math.min(mStartY, mEndY); 28 | // float centreX = Math.pow(radius, 2) - Math.pow() 29 | // float centreY = Math.abs((mEndY - mStartY)) / 2 + Math.min(mStartY, mEndY); 30 | canvas.drawCircle(centreX, centreY, c / 2, mPaint); 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | StringBuilder builder = new StringBuilder(); 36 | builder.append(PaintConfig.Shape.Circle).append(FIELD_SEPARATOR); 37 | builder.append(getFieldString()); 38 | return builder.toString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/paint/ImageShape.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo.paint; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | 6 | /** 7 | * Created by angcyo on 15-10-29-029. 8 | */ 9 | public class ImageShape extends PaintShape { 10 | 11 | 12 | public ImageShape(float mStartX, float mStartY, Paint mPaint) { 13 | super(mStartX, mStartY, mPaint); 14 | } 15 | 16 | public ImageShape(Paint paint) { 17 | super(paint); 18 | } 19 | 20 | @Override 21 | public void onDraw(Canvas canvas) { 22 | 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | StringBuilder builder = new StringBuilder(); 28 | builder.append(PaintConfig.Shape.Image).append(FIELD_SEPARATOR); 29 | builder.append(getFieldString()); 30 | return builder.toString(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/paint/LineShape.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo.paint; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | 6 | /** 7 | * Created by angcyo on 15-10-29-029. 8 | */ 9 | public class LineShape extends PaintShape { 10 | 11 | 12 | public LineShape(float mStartX, float mStartY, Paint mPaint) { 13 | super(mStartX, mStartY, mPaint); 14 | } 15 | 16 | public LineShape(Paint paint) { 17 | super(paint); 18 | } 19 | 20 | @Override 21 | public void onDraw(Canvas canvas) { 22 | canvas.drawLine(mStartX, mStartY, mEndX, mEndY, mPaint); 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | StringBuilder builder = new StringBuilder(); 28 | builder.append(PaintConfig.Shape.Line).append(FIELD_SEPARATOR); 29 | builder.append(getFieldString()); 30 | return builder.toString(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/paint/PaintConfig.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo.paint; 2 | 3 | import android.graphics.Color; 4 | import android.support.annotation.ColorInt; 5 | 6 | /** 7 | * Created by angcyo on 15-10-29-029. 8 | */ 9 | public class PaintConfig { 10 | static PaintConfig instance; 11 | /** 12 | * 画布宽高 13 | */ 14 | int surfaceWidth, surfaceHeight; 15 | /** 16 | * 当前选择的形状 17 | */ 18 | int currentShape; 19 | /** 20 | * 当前选择的形状颜色 21 | */ 22 | int currentShapeColor; 23 | /** 24 | * 当前选择的形状宽度 25 | */ 26 | float currentShapeWidth; 27 | /** 28 | * 画布背景颜色 29 | */ 30 | @ColorInt 31 | int surfaceBackgroundColor; 32 | 33 | /** 34 | * 字体大小 35 | */ 36 | float fontSize; 37 | 38 | private PaintConfig() { 39 | currentShape = Shape.Point; 40 | currentShapeColor = Color.GREEN; 41 | currentShapeWidth = 10f; 42 | 43 | fontSize = 30f; 44 | surfaceBackgroundColor = Color.WHITE; 45 | } 46 | 47 | public static PaintConfig getInstance() { 48 | if (instance == null) { 49 | synchronized (PaintConfig.class) { 50 | if (instance == null) { 51 | instance = new PaintConfig(); 52 | } 53 | } 54 | } 55 | return instance; 56 | } 57 | 58 | public int getSurfaceWidth() { 59 | return surfaceWidth; 60 | } 61 | 62 | public void setSurfaceWidth(int surfaceWidth) { 63 | this.surfaceWidth = surfaceWidth; 64 | } 65 | 66 | public int getSurfaceHeight() { 67 | return surfaceHeight; 68 | } 69 | 70 | public void setSurfaceHeight(int surfaceHeight) { 71 | this.surfaceHeight = surfaceHeight; 72 | } 73 | 74 | public int getCurrentShape() { 75 | return currentShape; 76 | } 77 | 78 | public void setCurrentShape(int currentShape) { 79 | this.currentShape = currentShape; 80 | } 81 | 82 | public int getCurrentShapeColor() { 83 | return currentShapeColor; 84 | } 85 | 86 | public void setCurrentShapeColor(int currentShapeColor) { 87 | this.currentShapeColor = currentShapeColor; 88 | } 89 | 90 | public float getCurrentShapeWidth() { 91 | return currentShapeWidth; 92 | } 93 | 94 | public void setCurrentShapeWidth(float currentShapeWidth) { 95 | this.currentShapeWidth = currentShapeWidth; 96 | } 97 | 98 | public int getSurfaceBackgroundColor() { 99 | return surfaceBackgroundColor; 100 | } 101 | 102 | public void setSurfaceBackgroundColor(int surfaceBackgroundColor) { 103 | this.surfaceBackgroundColor = surfaceBackgroundColor; 104 | } 105 | 106 | public float getFontSize() { 107 | return fontSize; 108 | } 109 | 110 | public void setFontSize(float fontSize) { 111 | this.fontSize = fontSize; 112 | } 113 | 114 | public final static class Shape { 115 | /** 116 | * 点 117 | */ 118 | public final static int Point = 1; 119 | /** 120 | * 线 121 | */ 122 | public final static int Line = 2; 123 | /** 124 | * 圆 125 | */ 126 | public final static int Circle = 3; 127 | /** 128 | * 矩形 129 | */ 130 | public final static int Rect = 4; 131 | /** 132 | * 图片 133 | */ 134 | public final static int Image = 5; 135 | 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/paint/PaintShape.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo.paint; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import android.text.TextUtils; 6 | 7 | /** 8 | * Created by angcyo on 15-10-29-029. 9 | */ 10 | public abstract class PaintShape { 11 | public static final String FIELD_SHAPE = "shape"; 12 | public static final String FIELD_PAINT_COL = "paint_col"; 13 | public static final String FIELD_PAINT_WIDTH = "paint_width"; 14 | public static final String FIELD_PAINT_STYLE = "paint_style"; 15 | public static final String FIELD_STARTXY = "start_xy"; 16 | public static final String FIELD_ENDXY = "end_xy"; 17 | 18 | public static final String FIELD_SEPARATOR_REG = "\\|";//匹配用 19 | public static final String FIELD_SEPARATOR = "|";//分隔图形的字段 20 | public static final String SHAPE_SEPARATOR = "@";//分隔不同的图形 21 | public static final String VALUE_SEPARATOR = ":";//分隔字段 22 | 23 | protected float mStartX, mStartY, mEndX, mEndY; 24 | protected Paint mPaint; 25 | 26 | public PaintShape(Paint paint) { 27 | this.mPaint = paint; 28 | } 29 | 30 | public PaintShape(float mStartX, float mStartY, Paint mPaint) { 31 | this.mStartX = mStartX; 32 | this.mStartY = mStartY; 33 | this.mPaint = mPaint; 34 | } 35 | 36 | public static PaintShape generateShape(String string) throws Exception { 37 | PaintShape shape = null; 38 | if (!TextUtils.isEmpty(string)) { 39 | String[] fields = string.split(FIELD_SEPARATOR_REG); 40 | 41 | //解析画笔 42 | Paint paint = new Paint(); 43 | paint.setColor(Integer.parseInt(fields[1])); 44 | paint.setStrokeWidth(Float.parseFloat(fields[2])); 45 | String style = fields[3]; 46 | if (style.equalsIgnoreCase("FILL")) { 47 | paint.setStyle(Paint.Style.FILL); 48 | } else if (style.equalsIgnoreCase("STROKE")) { 49 | paint.setStyle(Paint.Style.STROKE); 50 | } else if (style.equalsIgnoreCase("FILL_AND_STROKE")) { 51 | paint.setStyle(Paint.Style.FILL_AND_STROKE); 52 | } 53 | 54 | //解析图形 55 | String startXY, endXY; 56 | startXY = fields[4]; 57 | endXY = fields[5]; 58 | 59 | String[] sXY, eXY; 60 | sXY = startXY.split(VALUE_SEPARATOR); 61 | eXY = endXY.split(VALUE_SEPARATOR); 62 | 63 | switch (Integer.parseInt(fields[0])) { 64 | case PaintConfig.Shape.Point: 65 | shape = new PointShape(Float.parseFloat(sXY[0]), Float.parseFloat(sXY[1]), paint); 66 | break; 67 | case PaintConfig.Shape.Line: 68 | shape = new LineShape(Float.parseFloat(sXY[0]), Float.parseFloat(sXY[1]), paint); 69 | break; 70 | case PaintConfig.Shape.Circle: 71 | shape = new CircleShape(Float.parseFloat(sXY[0]), Float.parseFloat(sXY[1]), paint); 72 | break; 73 | case PaintConfig.Shape.Rect: 74 | shape = new RectShape(Float.parseFloat(sXY[0]), Float.parseFloat(sXY[1]), paint); 75 | break; 76 | case PaintConfig.Shape.Image: 77 | shape = new ImageShape(Float.parseFloat(sXY[0]), Float.parseFloat(sXY[1]), paint); 78 | break; 79 | default: 80 | break; 81 | } 82 | 83 | shape.setmEndX(Float.parseFloat(eXY[0])); 84 | shape.setmEndY(Float.parseFloat(eXY[1])); 85 | } 86 | 87 | return shape; 88 | } 89 | 90 | public float getmStartX() { 91 | return mStartX; 92 | } 93 | 94 | public void setmStartX(float mStartX) { 95 | this.mStartX = mStartX; 96 | } 97 | 98 | public float getmStartY() { 99 | return mStartY; 100 | } 101 | 102 | public void setmStartY(float mStartY) { 103 | this.mStartY = mStartY; 104 | } 105 | 106 | public float getmEndX() { 107 | return mEndX; 108 | } 109 | 110 | public void setmEndX(float mEndX) { 111 | this.mEndX = mEndX; 112 | } 113 | 114 | public float getmEndY() { 115 | return mEndY; 116 | } 117 | 118 | public void setmEndY(float mEndY) { 119 | this.mEndY = mEndY; 120 | } 121 | 122 | public abstract void onDraw(Canvas canvas); 123 | 124 | protected String getFieldString() { 125 | StringBuilder builder = new StringBuilder(); 126 | builder.append(mPaint.getColor()).append(FIELD_SEPARATOR); 127 | builder.append(mPaint.getStrokeWidth()).append(FIELD_SEPARATOR); 128 | builder.append(mPaint.getStyle()).append(FIELD_SEPARATOR); 129 | builder.append(mStartX).append(VALUE_SEPARATOR).append(mStartY).append(FIELD_SEPARATOR); 130 | builder.append(mEndX).append(VALUE_SEPARATOR).append(mEndY); 131 | return builder.toString(); 132 | } 133 | 134 | @Override 135 | public String toString() { 136 | return getFieldString(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/paint/PaintView.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo.paint; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.graphics.PixelFormat; 10 | import android.graphics.PorterDuff; 11 | import android.graphics.Rect; 12 | import android.os.Bundle; 13 | import android.os.Environment; 14 | import android.support.annotation.ColorInt; 15 | import android.support.v4.content.LocalBroadcastManager; 16 | import android.util.AttributeSet; 17 | import android.view.MotionEvent; 18 | import android.view.SurfaceHolder; 19 | import android.view.SurfaceView; 20 | import android.view.View; 21 | 22 | import com.angcyo.paintdemo.socket.ClientSocket; 23 | 24 | import java.io.File; 25 | import java.io.FileOutputStream; 26 | import java.util.UUID; 27 | import java.util.Vector; 28 | 29 | /** 30 | * Created by angcyo on 15-10-29-029. 31 | */ 32 | public class PaintView extends SurfaceView implements SurfaceHolder.Callback, View.OnTouchListener, ClientSocket.OnDataChangeListener { 33 | public static final String TAG = "PaintView"; 34 | public static final String BDC_CAPTURE = "bdc_capture";// 35 | public static final String CAPTURE_PATH = "file_path"; 36 | public static final String CAPTURE_CODE = "capture_code"; 37 | 38 | public static boolean isRunning = true; 39 | public static volatile boolean isChange = true;//先画一次 40 | public static volatile boolean isSendToServer = true;//是否是发送出去 41 | SurfaceHolder mSurfaceHolder; 42 | @ColorInt 43 | int mViewBGCol; 44 | float downX, downY; 45 | float moveX, moveY; 46 | Paint mFpsPaint; 47 | float fpsCount = 0; 48 | long startTime = 0; 49 | float fps; 50 | Vector mShapes;//所有需要绘制的数据 51 | Vector mBackShapes;//所有需要绘制的数据 52 | PaintShape mShape; 53 | volatile Rect mDirtyRect; 54 | Bitmap captureBmp; 55 | LocalBroadcastManager localBroadcastManager; 56 | private boolean isCapture = false; 57 | 58 | private ClientSocket mClientSocket; 59 | 60 | public PaintView(Context context) { 61 | this(context, null); 62 | } 63 | 64 | public PaintView(Context context, AttributeSet attrs) { 65 | this(context, attrs, 0); 66 | } 67 | 68 | public PaintView(Context context, AttributeSet attrs, int defStyleAttr) { 69 | super(context, attrs, defStyleAttr); 70 | init(); 71 | } 72 | 73 | private void init() { 74 | mViewBGCol = PaintConfig.getInstance().getSurfaceBackgroundColor(); 75 | mSurfaceHolder = getHolder(); 76 | mSurfaceHolder.addCallback(this); 77 | setOnTouchListener(this); 78 | 79 | mShapes = new Vector<>(); 80 | // 81 | mFpsPaint = new Paint(); 82 | mFpsPaint.setColor(Color.RED); 83 | // mFpsPaint.setStrokeWidth(20f); 84 | mFpsPaint.setTextSize(30f); 85 | mFpsPaint.setAntiAlias(true); 86 | // setBackgroundColor(mViewBGCol); 87 | 88 | localBroadcastManager = LocalBroadcastManager.getInstance(getContext()); 89 | } 90 | 91 | @Override 92 | public void surfaceCreated(SurfaceHolder holder) { 93 | // Log.e(TAG, "surfaceCreated"); 94 | // setBackgroundColor(mViewBGCol); 95 | isRunning = true; 96 | mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT); 97 | // mDirtyRect = new Rect((int) downX - 10, (int) downY - 10, (int) downX + 10, (int) downY + 10); 98 | // new Thread(this).start(); 99 | } 100 | 101 | @Override 102 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 103 | isChange = true; 104 | PaintConfig.getInstance().setSurfaceWidth(width); 105 | PaintConfig.getInstance().setSurfaceHeight(height); 106 | 107 | doDraw(); 108 | // Log.e(TAG, "surfaceChanged"); 109 | 110 | // Canvas canvas = mSurfaceHolder.lockCanvas(); 111 | // canvas.drawLine(2.0f, 2.0f, 500.0f, 500f, mFpsPaint); 112 | // mSurfaceHolder.unlockCanvasAndPost(canvas); 113 | } 114 | 115 | @Override 116 | public void surfaceDestroyed(SurfaceHolder holder) { 117 | // Log.e(TAG, "surfaceDestroyed"); 118 | isRunning = false; 119 | 120 | disconnectServer(); 121 | } 122 | 123 | @Override 124 | public boolean onTouch(View v, MotionEvent event) { 125 | switch (event.getAction()) { 126 | case MotionEvent.ACTION_DOWN: 127 | downX = event.getX(); 128 | downY = event.getY(); 129 | mShape = generateShape(downX, downY); 130 | mShape.setmEndX(downX); 131 | mShape.setmEndY(downY); 132 | mShapes.add(mShape); 133 | mDirtyRect = new Rect((int) downX - 10, (int) downY - 10, (int) downX + 10, (int) downY + 10); 134 | break; 135 | case MotionEvent.ACTION_MOVE: 136 | moveX = event.getX(); 137 | moveY = event.getY(); 138 | if (PaintConfig.getInstance().getCurrentShape() == PaintConfig.Shape.Point) { 139 | PaintShape shape = generateShape(moveX, moveY); 140 | shape.setmEndX(mShapes.lastElement().getmStartX()); 141 | shape.setmEndY(mShapes.lastElement().getmStartY()); 142 | mShapes.add(shape); 143 | } else { 144 | mShape.setmEndX(moveX); 145 | mShape.setmEndY(moveY); 146 | } 147 | int left = (int) Math.min(downX, moveX); 148 | int top = (int) Math.min(downY, moveY); 149 | int right = (int) Math.max(downX, moveX); 150 | int bottom = (int) Math.max(downY, moveY); 151 | mDirtyRect = new Rect(left - 10, top - 10, right + 10, bottom + 10); 152 | break; 153 | case MotionEvent.ACTION_UP: 154 | mDirtyRect = null; 155 | break; 156 | } 157 | isChange = true; 158 | isSendToServer = true; 159 | doDraw(); 160 | return true; 161 | } 162 | 163 | private PaintShape generateShape(float downX, float downY) { 164 | PaintShape shape; 165 | Paint paint = new Paint(); 166 | paint.setColor(PaintConfig.getInstance().getCurrentShapeColor()); 167 | paint.setStrokeWidth(PaintConfig.getInstance().getCurrentShapeWidth()); 168 | switch (PaintConfig.getInstance().getCurrentShape()) { 169 | case PaintConfig.Shape.Point: 170 | shape = new PointShape(downX, downY, paint); 171 | break; 172 | case PaintConfig.Shape.Line: 173 | shape = new LineShape(downX, downY, paint); 174 | break; 175 | case PaintConfig.Shape.Circle: 176 | paint.setStyle(Paint.Style.STROKE); 177 | shape = new CircleShape(downX, downY, paint); 178 | break; 179 | case PaintConfig.Shape.Rect: 180 | paint.setStyle(Paint.Style.STROKE); 181 | shape = new RectShape(downX, downY, paint); 182 | break; 183 | case PaintConfig.Shape.Image: 184 | shape = new ImageShape(downX, downY, paint); 185 | break; 186 | default: 187 | shape = new PointShape(downX, downY, paint); 188 | break; 189 | } 190 | return shape; 191 | } 192 | 193 | public void doDraw() { 194 | synchronized (this) { 195 | if (isChange) { 196 | //有变化重新绘制 197 | try { 198 | mBackShapes = (Vector) mShapes.clone(); 199 | if (isSendToServer) { 200 | try { 201 | ClientSocket.updateWriteData(mBackShapes); 202 | isSendToServer = false; 203 | } catch (Exception e) { 204 | e.printStackTrace(); 205 | } 206 | } 207 | } catch (Exception e) { 208 | } 209 | Canvas canvas = null; 210 | try { 211 | canvas = mSurfaceHolder.lockCanvas(); 212 | drawBg(canvas); 213 | drawContent(canvas, mBackShapes); 214 | 215 | try { 216 | drawSvrContent(canvas, ClientSocket.getReadData()); 217 | } catch (Exception e) { 218 | } 219 | } catch (Exception e) { 220 | } finally { 221 | if (canvas != null) { 222 | mSurfaceHolder.unlockCanvasAndPost(canvas); 223 | } 224 | } 225 | isChange = false; 226 | } 227 | 228 | //截图 229 | if (isCapture) { 230 | try { 231 | if (captureBmp != null) { 232 | captureBmp.recycle(); 233 | } 234 | captureBmp = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); 235 | Canvas bitCanvas = new Canvas(captureBmp); 236 | drawBg(bitCanvas); 237 | drawContent(bitCanvas, mBackShapes); 238 | try { 239 | drawSvrContent(bitCanvas, ClientSocket.getReadData()); 240 | } catch (Exception e) { 241 | } 242 | // drawFps(bitCanvas); 243 | isCapture = false; 244 | saveBitmap(); 245 | } catch (Exception e) { 246 | 247 | } 248 | } 249 | } 250 | 251 | } 252 | 253 | private void disconnectServer() { 254 | if (mClientSocket != null) { 255 | mClientSocket.exit(); 256 | } 257 | } 258 | 259 | public void connectServer() { 260 | if (mClientSocket == null) { 261 | mClientSocket = new ClientSocket(); 262 | mClientSocket.setDataChangeListener(this); 263 | } 264 | new Thread(mClientSocket).start(); 265 | } 266 | 267 | public void setClientSocket(ClientSocket clientSocket) { 268 | if (mClientSocket == null) { 269 | mClientSocket = clientSocket; 270 | mClientSocket.setDataChangeListener(this); 271 | } 272 | } 273 | 274 | private void saveBitmap() { 275 | Intent intent = new Intent(BDC_CAPTURE); 276 | Bundle bundle = new Bundle(); 277 | try { 278 | String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + File.separator + UUID.randomUUID() + ".jpg"; 279 | File file = new File(filePath); 280 | FileOutputStream fos = new FileOutputStream(file); 281 | captureBmp.compress(Bitmap.CompressFormat.JPEG, 100, fos); 282 | fos.close(); 283 | bundle.putInt(CAPTURE_CODE, 1); 284 | bundle.putString(CAPTURE_PATH, filePath); 285 | } catch (Exception e) { 286 | bundle.putInt(CAPTURE_CODE, 0); 287 | } 288 | intent.putExtras(bundle); 289 | localBroadcastManager.sendBroadcast(intent); 290 | } 291 | 292 | /** 293 | * 绘制本地图形 294 | */ 295 | private void drawContent(Canvas canvas, Vector shapes) { 296 | if (shapes == null) { 297 | return; 298 | } 299 | for (PaintShape shape : shapes) { 300 | shape.onDraw(canvas); 301 | } 302 | } 303 | 304 | /** 305 | * 绘制服务端发送过来的图形 306 | */ 307 | private void drawSvrContent(Canvas canvas, Vector shapes) { 308 | if (shapes == null) { 309 | return; 310 | } 311 | for (PaintShape shape : shapes) { 312 | shape.onDraw(canvas); 313 | } 314 | } 315 | 316 | private void drawBg(Canvas canvas) { 317 | canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);//清屏 318 | canvas.drawColor(PaintConfig.getInstance().getSurfaceBackgroundColor());//绘制背景 319 | } 320 | 321 | private void drawFps(Canvas canvas) { 322 | fpsCount++; 323 | long nowTime = System.currentTimeMillis(); 324 | if (nowTime - startTime > 1000) { 325 | fps = fpsCount / (nowTime - startTime) * 1000; 326 | startTime = nowTime; 327 | fpsCount = 0; 328 | } 329 | canvas.drawText(String.format("FPS:%.3f", fps), PaintConfig.getInstance().getSurfaceWidth() - 200, 40, mFpsPaint); 330 | } 331 | 332 | public void removeLastOne() { 333 | if (mShapes != null && mShapes.size() > 0) { 334 | mShapes.remove(mShapes.size() - 1); 335 | isChange = true; 336 | isSendToServer = true; 337 | doDraw(); 338 | } 339 | } 340 | 341 | public void removeAll() { 342 | if (mShapes != null && mShapes.size() > 0) { 343 | mShapes.clear(); 344 | isChange = true; 345 | isSendToServer = true; 346 | doDraw(); 347 | } 348 | } 349 | 350 | /** 351 | * 截屏 352 | */ 353 | public void capture() { 354 | isCapture = true; 355 | doDraw(); 356 | } 357 | 358 | @Override 359 | public void onDataChange() { 360 | isChange = true; 361 | isSendToServer = false; 362 | doDraw(); 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/paint/PointShape.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo.paint; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | 6 | /** 7 | * Created by angcyo on 15-10-29-029. 8 | */ 9 | public class PointShape extends PaintShape { 10 | 11 | 12 | public PointShape(float mStartX, float mStartY, Paint mPaint) { 13 | super(mStartX, mStartY, mPaint); 14 | } 15 | 16 | public PointShape(Paint paint) { 17 | super(paint); 18 | } 19 | 20 | @Override 21 | public void onDraw(Canvas canvas) { 22 | Paint paint = new Paint(mPaint); 23 | paint.setStrokeWidth(mPaint.getStrokeWidth()); 24 | canvas.drawLine(mStartX, mStartY, mEndX, mEndY, paint); 25 | canvas.drawCircle(mStartX, mStartY, mPaint.getStrokeWidth() / 2, mPaint); 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | StringBuilder builder = new StringBuilder(); 31 | builder.append(PaintConfig.Shape.Point).append(FIELD_SEPARATOR); 32 | builder.append(getFieldString()); 33 | return builder.toString(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/paint/RectShape.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo.paint; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | 6 | /** 7 | * Created by angcyo on 15-10-29-029. 8 | */ 9 | public class RectShape extends PaintShape { 10 | 11 | 12 | public RectShape(float mStartX, float mStartY, Paint mPaint) { 13 | super(mStartX, mStartY, mPaint); 14 | } 15 | 16 | public RectShape(Paint paint) { 17 | super(paint); 18 | } 19 | 20 | @Override 21 | public void onDraw(Canvas canvas) { 22 | canvas.drawRect(mStartX, mStartY, mEndX, mEndY, mPaint); 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | StringBuilder builder = new StringBuilder(); 28 | builder.append(PaintConfig.Shape.Rect).append(FIELD_SEPARATOR); 29 | builder.append(getFieldString()); 30 | return builder.toString(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/socket/ClientSocket.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo.socket; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.text.TextUtils; 6 | import android.util.Log; 7 | 8 | import com.angcyo.paintdemo.MainActivity; 9 | import com.angcyo.paintdemo.paint.PaintShape; 10 | 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.OutputStream; 14 | import java.net.InetSocketAddress; 15 | import java.net.Socket; 16 | import java.util.Vector; 17 | 18 | /** 19 | * Created by angcyo on 15-10-30-030. 20 | */ 21 | public class ClientSocket implements Runnable { 22 | private static Vector writeData; 23 | private static Vector readData; 24 | private static Object lock = new Object(); 25 | InputStream inputStream; 26 | OutputStream outputStream; 27 | private boolean isRunning = true; 28 | private boolean isReadWrite = true; 29 | private Socket mSocket; 30 | private ReadRunnable readRunnable; 31 | private WriteRunnable writeRunnable; 32 | private OnDataChangeListener listener; 33 | 34 | public ClientSocket() { 35 | } 36 | 37 | public ClientSocket(Socket mSocket) throws IOException { 38 | this.mSocket = mSocket; 39 | startReadWriteThread(); 40 | } 41 | 42 | public static void updateWriteData(Vector newData) { 43 | writeData = (Vector) newData.clone(); 44 | synchronized (lock) { 45 | lock.notifyAll(); 46 | } 47 | } 48 | 49 | private static synchronized void updateReadData(Vector newData) { 50 | readData = newData; 51 | } 52 | 53 | public static Vector getReadData() { 54 | return readData; 55 | } 56 | 57 | public void setDataChangeListener(OnDataChangeListener listener) { 58 | this.listener = listener; 59 | } 60 | 61 | @Override 62 | public void run() { 63 | while (isRunning) { 64 | //连接服务器 65 | if (mSocket == null) { 66 | try { 67 | connect(); 68 | } catch (IOException e) { 69 | mSocket = null; 70 | try { 71 | Thread.sleep(100); 72 | continue; 73 | } catch (InterruptedException e1) { 74 | } 75 | } 76 | } 77 | Thread.yield(); 78 | try { 79 | Thread.sleep(500); 80 | } catch (InterruptedException e) { 81 | } 82 | } 83 | try { 84 | disconnect(); 85 | } catch (IOException e) { 86 | } 87 | } 88 | 89 | public void exit() { 90 | isRunning = false; 91 | } 92 | 93 | /** 94 | * 连接服务器 95 | */ 96 | private void connect() throws IOException { 97 | if (mSocket == null) { 98 | mSocket = new Socket(); 99 | mSocket.setSoTimeout(SocketConfig.READ_TIME_OUT); 100 | } 101 | mSocket.connect(new InetSocketAddress(SocketConfig.SVR_IP, SocketConfig.SVR_PORT), SocketConfig.CONNECT_TIME_OUT); 102 | startReadWriteThread(); 103 | sendBroadcast(); 104 | } 105 | 106 | private void sendBroadcast() { 107 | Intent intent = new Intent(SocketConfig.BDC_CONNECT_SERVER); 108 | Bundle bundle = new Bundle(); 109 | bundle.putString(SocketConfig.KEY_SERVER_IP, SocketConfig.SVR_IP); 110 | intent.putExtras(bundle); 111 | MainActivity.localBroadcastManager.sendBroadcast(intent); 112 | } 113 | 114 | private void startReadWriteThread() throws IOException { 115 | inputStream = mSocket.getInputStream(); 116 | outputStream = mSocket.getOutputStream(); 117 | 118 | readRunnable = new ReadRunnable(); 119 | writeRunnable = new WriteRunnable(); 120 | 121 | new Thread(readRunnable).start(); 122 | new Thread(writeRunnable).start(); 123 | } 124 | 125 | /** 126 | * 断开服务器 127 | */ 128 | private void disconnect() throws IOException { 129 | if (mSocket != null) { 130 | mSocket.close(); 131 | mSocket = null; 132 | isReadWrite = false; 133 | } 134 | } 135 | 136 | /** 137 | * 处理收到的数据 138 | */ 139 | private void handleData(String data) { 140 | if (TextUtils.isEmpty(data)) { 141 | return; 142 | } 143 | String[] shapes = data.split(PaintShape.SHAPE_SEPARATOR); 144 | Vector paintShapes = new Vector<>(); 145 | for (String shapeStr : shapes) { 146 | try { 147 | shapeStr = shapeStr.replaceAll(SocketConfig.FLAG_SOCKET_READ_END, ""); 148 | PaintShape paintShape = PaintShape.generateShape(shapeStr); 149 | paintShapes.add(paintShape); 150 | } catch (Exception e) { 151 | 152 | } 153 | } 154 | updateReadData(paintShapes); 155 | 156 | if (listener != null) { 157 | listener.onDataChange(); 158 | } 159 | } 160 | 161 | public interface OnDataChangeListener { 162 | void onDataChange(); 163 | } 164 | 165 | /** 166 | * 数据读线程 167 | */ 168 | private class ReadRunnable implements Runnable { 169 | @Override 170 | public void run() {//读线程 171 | int len; 172 | byte[] bytes = new byte[1024]; 173 | StringBuilder stringBuilder = new StringBuilder(); 174 | String line; 175 | 176 | while (isReadWrite) { 177 | try { 178 | len = inputStream.read(bytes); 179 | line = new String(bytes, 0, len); 180 | stringBuilder.append(line); 181 | if (line.endsWith(SocketConfig.FLAG_SOCKET_READ_END)) { 182 | StringBuffer buffer = new StringBuffer(stringBuilder.toString()); 183 | Log.e("收到数据: " + buffer.length(), buffer.toString()); 184 | handleData(buffer.toString()); 185 | stringBuilder = new StringBuilder(); 186 | } 187 | } catch (Exception e) { 188 | e.printStackTrace(); 189 | } 190 | } 191 | } 192 | } 193 | 194 | /** 195 | * 数据写线程 196 | */ 197 | private class WriteRunnable implements Runnable { 198 | 199 | @Override 200 | public void run() {//写数据 201 | while (isReadWrite) { 202 | if (writeData == null || writeData.size() < 1) { 203 | synchronized (lock) { 204 | try { 205 | lock.wait(); 206 | } catch (InterruptedException e) { 207 | 208 | } 209 | } 210 | } else { 211 | try { 212 | while (writeData.size() > 0) { 213 | PaintShape shape = writeData.remove(0); 214 | outputStream.write(shape.toString().getBytes()); 215 | outputStream.write(PaintShape.SHAPE_SEPARATOR.getBytes()); 216 | } 217 | outputStream.write(SocketConfig.FLAG_SOCKET_READ_END.getBytes()); 218 | outputStream.flush(); 219 | writeData.clear(); 220 | writeData = null; 221 | } catch (Exception e) { 222 | try { 223 | disconnect(); 224 | } catch (IOException e1) { 225 | } 226 | } 227 | } 228 | } 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/socket/ServerSocket.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo.socket; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | 6 | import com.angcyo.paintdemo.MainActivity; 7 | import com.angcyo.paintdemo.paint.PaintView; 8 | 9 | import java.io.IOException; 10 | import java.net.Socket; 11 | 12 | /** 13 | * Created by angcyo on 15-10-30-030. 14 | */ 15 | public class ServerSocket implements Runnable { 16 | 17 | java.net.ServerSocket mServerSocket; 18 | Socket mClient; 19 | ClientSocket mClientSocket; 20 | PaintView paintView; 21 | 22 | private boolean isRunning = true; 23 | 24 | public ServerSocket(PaintView paintView) { 25 | this.paintView = paintView; 26 | } 27 | 28 | public void exit() { 29 | isRunning = false; 30 | } 31 | 32 | @Override 33 | public void run() { 34 | while (isRunning) { 35 | if (mClient == null) { 36 | try { 37 | mClient = accept();//监听连接 38 | SocketConfig.CLIENT_IP = mClient.getInetAddress().getHostAddress(); 39 | sendBroadcast(); 40 | } catch (IOException e) { 41 | mClient = null; 42 | mServerSocket = null; 43 | } 44 | } 45 | if (mClientSocket == null && mClient != null) { 46 | try { 47 | mClientSocket = new ClientSocket(mClient); 48 | paintView.setClientSocket(mClientSocket); 49 | } catch (IOException e) { 50 | try { 51 | mClient.close(); 52 | } catch (IOException e1) { 53 | } 54 | mClient = null; 55 | mClientSocket = null; 56 | } 57 | } 58 | 59 | Thread.yield(); 60 | try { 61 | Thread.sleep(500); 62 | } catch (InterruptedException e) { 63 | } 64 | } 65 | disconnect(); 66 | } 67 | 68 | private void sendBroadcast() { 69 | Intent intent = new Intent(SocketConfig.BDC_CONNECT_CLIENT); 70 | Bundle bundle = new Bundle(); 71 | bundle.putString(SocketConfig.KEY_CLIENT_IP, SocketConfig.CLIENT_IP); 72 | intent.putExtras(bundle); 73 | MainActivity.localBroadcastManager.sendBroadcast(intent); 74 | } 75 | 76 | 77 | private void disconnect() { 78 | mClientSocket.exit(); 79 | try { 80 | mClient.close(); 81 | } catch (IOException e) { 82 | } 83 | try { 84 | mServerSocket.close(); 85 | } catch (IOException e) { 86 | e.printStackTrace(); 87 | } 88 | 89 | mClientSocket = null; 90 | mClient = null; 91 | mServerSocket = null; 92 | } 93 | 94 | private Socket accept() throws IOException { 95 | if (mServerSocket == null) { 96 | mServerSocket = new java.net.ServerSocket(SocketConfig.SVR_PORT); 97 | } 98 | return mServerSocket.accept(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/com/angcyo/paintdemo/socket/SocketConfig.java: -------------------------------------------------------------------------------- 1 | package com.angcyo.paintdemo.socket; 2 | 3 | /** 4 | * Created by angcyo on 15-10-30-030. 5 | */ 6 | public class SocketConfig { 7 | /** 8 | * 服务端ip 9 | */ 10 | public static String SVR_IP = "192.168.1.100"; 11 | /** 12 | * 服务端端口 13 | */ 14 | public static int SVR_PORT = 8720; 15 | 16 | public static int READ_TIME_OUT = 10000; 17 | public static int CONNECT_TIME_OUT = 10000; 18 | 19 | public static String FLAG_SOCKET_READ_END = "!end!";//数据分隔标示 20 | 21 | public static boolean isServer = false;//当前app属于哪种状态 22 | // public static boolean isStartClient = false;//客户端启动连接 23 | 24 | public static String BDC_CONNECT_SERVER = "connect_server";//连上服务器,发送的广播 25 | public static String BDC_CONNECT_CLIENT = "connect_client";//客户端连上,发送的广播 26 | public static String KEY_SERVER_IP = "server_ip"; 27 | public static String KEY_CLIENT_IP = "client_ip"; 28 | public static String CLIENT_IP = "192.168.1.100"; 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/demo/Demo.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | /** 4 | * Created by robi on 2016-04-22 14:55. 5 | */ 6 | public class Demo { 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/demo/Demo2.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | /** 4 | * Created by robi on 2016-04-22 15:06. 5 | */ 6 | public class Demo2 { 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/nt/finger/paint/DrawView.java: -------------------------------------------------------------------------------- 1 | package nt.finger.paint; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.util.Log; 8 | import android.view.MotionEvent; 9 | import android.view.View; 10 | import android.view.View.OnTouchListener; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Random; 15 | 16 | public class DrawView extends View implements OnTouchListener { 17 | private static final String TAG = "DrawView"; 18 | 19 | List points = new ArrayList(); 20 | Paint paint = new Paint(); 21 | Random gen; 22 | int col_mode; 23 | int wid_mode; 24 | 25 | public DrawView(Context context) { 26 | super(context); 27 | 28 | // set default colour to white 29 | col_mode = 0; 30 | 31 | // set default width to 7px 32 | wid_mode = 10; 33 | 34 | setFocusable(true); 35 | setFocusableInTouchMode(true); 36 | 37 | this.setOnTouchListener(this); 38 | 39 | paint.setAntiAlias(true); 40 | } 41 | 42 | // used to clear the screen 43 | public void clearPoints () { 44 | points.clear(); 45 | forceRedraw(); 46 | } 47 | 48 | /** 49 | * Force view to redraw. Without this points aren't cleared until next action 50 | */ 51 | public void forceRedraw() { 52 | invalidate(); 53 | } 54 | 55 | // used to set drawing colour 56 | public void changeColour (int col_in) { 57 | col_mode = col_in; 58 | } 59 | 60 | // used to set drawing width 61 | public void changeWidth (int wid_in) { 62 | wid_mode = wid_in; 63 | } 64 | 65 | @Override 66 | public void onDraw(Canvas canvas) { 67 | // for each point, draw on canvas 68 | for (Point point : points) { 69 | point.draw(canvas, paint); 70 | Log.d(TAG, "pointcount: " + points.size()); 71 | } 72 | } 73 | 74 | public boolean onTouch(View view, MotionEvent event) { 75 | int new_col = 0; 76 | if (col_mode < 0) { 77 | gen = new Random(); 78 | col_mode = gen.nextInt( 8 ); 79 | } 80 | // This if statement may be redundant now 81 | if (col_mode >= 0) { 82 | switch (col_mode) { 83 | case 0 : { 84 | new_col = Color.WHITE; 85 | break; 86 | } 87 | case 1 : { 88 | new_col = Color.BLUE; 89 | break; 90 | } 91 | case 2 : { 92 | new_col = Color.CYAN; 93 | break; 94 | } 95 | case 3 : { 96 | new_col = Color.GREEN; 97 | break; 98 | } 99 | case 4 : { 100 | new_col = Color.MAGENTA; 101 | break; 102 | } 103 | case 5 : { 104 | new_col = Color.RED; 105 | break; 106 | } 107 | case 6 : { 108 | new_col = Color.YELLOW; 109 | break; 110 | } 111 | case 7 : { 112 | new_col = Color.BLACK; 113 | break; 114 | } 115 | } 116 | } 117 | /* else { 118 | gen = new Random(); 119 | new_col = gen.nextInt( 8 ); 120 | } */ 121 | Point point; 122 | if(event.getAction() == MotionEvent.ACTION_MOVE) { 123 | point = new FriendlyPoint(event.getX(), event.getY(), new_col, points.get(points.size() - 1), wid_mode); 124 | } else if (event.getAction() == MotionEvent.ACTION_DOWN) { 125 | point = new Point(event.getX(), event.getY(), new_col, wid_mode); 126 | } else { 127 | return false; 128 | } 129 | points.add(point); 130 | forceRedraw(); 131 | Log.d(TAG, "point: " + point); 132 | return true; 133 | } 134 | } -------------------------------------------------------------------------------- /app/src/main/java/nt/finger/paint/FileChooser.java: -------------------------------------------------------------------------------- 1 | package nt.finger.paint; 2 | 3 | import java.io.File; 4 | import java.io.FilenameFilter; 5 | 6 | import android.app.Activity; 7 | import android.app.AlertDialog; 8 | import android.app.AlertDialog.Builder; 9 | import android.app.Dialog; 10 | import android.content.DialogInterface; 11 | import android.graphics.Color; 12 | import android.os.Bundle; 13 | import android.os.Environment; 14 | import android.util.Log; 15 | import android.view.Window; 16 | import android.view.WindowManager; 17 | 18 | public class FileChooser extends Activity { 19 | private static final String TAG = "FileChooser"; 20 | private String[] mFileList; 21 | private File mPath = new File(Environment.getExternalStorageDirectory() + "//yourdir//"); 22 | private String mChosenFile; 23 | private static final String JPG = ".jpg"; 24 | private static final String JPEG = ".jpeg"; 25 | private static final String PNG = ".png"; 26 | private static final int DIALOG_LOAD_FILE = 1000; 27 | 28 | @Override 29 | public void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | // Set full screen view 32 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 33 | requestWindowFeature(Window.FEATURE_NO_TITLE); 34 | 35 | loadFileList(); 36 | } 37 | 38 | private void loadFileList() { 39 | try { 40 | mPath.mkdirs(); 41 | } 42 | catch(SecurityException e) { 43 | Log.e(TAG, "unable to write on the SD card " + e.toString()); 44 | } 45 | 46 | if (mPath.exists()) { 47 | FilenameFilter filter = new FilenameFilter() { 48 | public boolean accept(File dir, String filename) { 49 | File sel = new File(dir, filename); 50 | return filename.contains(JPG) || filename.contains(JPEG) || filename.contains(PNG) || sel.isDirectory(); 51 | } 52 | }; 53 | mFileList = mPath.list(filter); 54 | } else { 55 | mFileList = new String[0]; 56 | } 57 | } 58 | 59 | protected Dialog onCreateDialog(int id) { 60 | Dialog dialog = null; 61 | Builder builder = new Builder(this); 62 | 63 | switch (id) { 64 | case DIALOG_LOAD_FILE : 65 | builder.setTitle("Choose your background"); 66 | if (mFileList == null) { 67 | Log.e(TAG, "Showing file chooser before loading the file list"); 68 | dialog = builder.create(); 69 | return dialog; 70 | } 71 | builder.setItems(mFileList, new DialogInterface.OnClickListener(){ 72 | public void onClick(DialogInterface dialog, int which){ 73 | mChosenFile = mFileList[which]; 74 | // do other shit with file 75 | } 76 | }); 77 | break; 78 | } 79 | dialog = builder.show(); 80 | return dialog; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/nt/finger/paint/FingerPaint.java: -------------------------------------------------------------------------------- 1 | package nt.finger.paint; 2 | 3 | import android.content.Intent; 4 | import android.database.Cursor; 5 | import android.graphics.Color; 6 | import android.graphics.drawable.Drawable; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.provider.MediaStore; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.util.Log; 12 | import android.view.Menu; 13 | import android.view.MenuInflater; 14 | import android.view.MenuItem; 15 | 16 | import com.angcyo.paintdemo.R; 17 | 18 | public class FingerPaint extends AppCompatActivity { 19 | private static final String TAG = "FingerPaint"; 20 | DrawView drawView; 21 | 22 | /** Called when the activity is first created. */ 23 | @Override 24 | public void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | // Set full screen view 27 | // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 28 | // requestWindowFeature(Window.FEATURE_NO_TITLE); 29 | 30 | // lock screen orientation (stops screen clearing when rotating phone) 31 | setRequestedOrientation(getResources().getConfiguration().orientation); 32 | 33 | drawView = new DrawView(this); 34 | setContentView(drawView); 35 | drawView.setBackgroundColor(Color.BLACK); 36 | drawView.requestFocus(); 37 | } 38 | 39 | @Override 40 | public boolean onCreateOptionsMenu(Menu menu) { 41 | MenuInflater inflater = getMenuInflater(); 42 | inflater.inflate(R.menu.paint_menu, menu); 43 | return true; 44 | } 45 | 46 | @Override 47 | public boolean onOptionsItemSelected(MenuItem item) { 48 | // Handle item selection 49 | switch (item.getItemId()) { 50 | case R.id.clear_id : { 51 | drawView.clearPoints(); 52 | return true; 53 | } 54 | case R.id.p_white_id : { 55 | drawView.changeColour(0); 56 | return true; 57 | } 58 | case R.id.p_blue_id : { 59 | drawView.changeColour(1); 60 | return true; 61 | } 62 | case R.id.p_lblue_id : { 63 | drawView.changeColour(2); 64 | return true; 65 | } 66 | case R.id.p_green_id : { 67 | drawView.changeColour(3); 68 | return true; 69 | } 70 | case R.id.p_pink_id : { 71 | drawView.changeColour(4); 72 | return true; 73 | } 74 | case R.id.p_red_id : { 75 | drawView.changeColour(5); 76 | return true; 77 | } 78 | case R.id.p_yellow_id : { 79 | drawView.changeColour(6); 80 | return true; 81 | } 82 | case R.id.p_black_id : { 83 | drawView.changeColour(7); 84 | return true; 85 | } 86 | case R.id.p_random_id : { 87 | drawView.changeColour(-1); 88 | return true; 89 | } 90 | case R.id.b_white_id : { 91 | drawView.setBackgroundColor(Color.WHITE); 92 | return true; 93 | } 94 | case R.id.b_blue_id : { 95 | drawView.setBackgroundColor(Color.BLUE); 96 | return true; 97 | } 98 | case R.id.b_lblue_id : { 99 | drawView.setBackgroundColor(Color.CYAN); 100 | return true; 101 | } 102 | case R.id.b_green_id : { 103 | drawView.setBackgroundColor(Color.GREEN); 104 | return true; 105 | } 106 | case R.id.b_pink_id : { 107 | drawView.setBackgroundColor(Color.MAGENTA); 108 | return true; 109 | } 110 | case R.id.b_red_id : { 111 | drawView.setBackgroundColor(Color.RED); 112 | return true; 113 | } 114 | case R.id.b_yellow_id : { 115 | drawView.setBackgroundColor(Color.YELLOW); 116 | return true; 117 | } 118 | case R.id.b_black_id : { 119 | drawView.setBackgroundColor(Color.BLACK); 120 | return true; 121 | } 122 | case R.id.b_custom_id : { 123 | setCustomBackground(drawView); 124 | return true; 125 | } 126 | case R.id.w_xsmall : { 127 | drawView.changeWidth(0); 128 | return true; 129 | } 130 | case R.id.w_small : { 131 | drawView.changeWidth(5); 132 | return true; 133 | } 134 | case R.id.w_medium : { 135 | drawView.changeWidth(10); 136 | return true; 137 | } 138 | case R.id.w_large : { 139 | drawView.changeWidth(15); 140 | return true; 141 | } 142 | case R.id.w_xlarge : { 143 | drawView.changeWidth(20); 144 | return true; 145 | } 146 | default : { 147 | return true; 148 | } 149 | } 150 | } 151 | 152 | void setCustomBackground(DrawView v) { 153 | Intent fileChooserIntent = new Intent(); 154 | fileChooserIntent.addCategory(Intent.CATEGORY_OPENABLE); 155 | fileChooserIntent.setType("image/*"); 156 | fileChooserIntent.setAction(Intent.ACTION_GET_CONTENT); 157 | startActivityForResult(Intent.createChooser(fileChooserIntent, "Select Picture"), 1); 158 | /* 159 | // menu option for setting a custom background 160 | String Url = "http://www.google.ca"; // http://www.google.ca 161 | Intent fileChooserIntent = new Intent(Intent.ACTION_CHOOSER, Uri.parse(Url)); 162 | this.startActivity(fileChooserIntent); 163 | */ 164 | } 165 | 166 | @Override 167 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 168 | // if statement prevents force close error when picture isn't selected 169 | if (resultCode == RESULT_OK) 170 | { 171 | Uri resultUri = data.getData(); 172 | //String resultString = data.getData().toString(); 173 | 174 | String drawString = resultUri.getPath(); 175 | String galleryString = getGalleryPath(resultUri); 176 | 177 | // if Gallery app was used 178 | if (galleryString != null) 179 | { 180 | Log.d(TAG, galleryString); 181 | drawString = galleryString; 182 | } 183 | // else another file manager was used 184 | else 185 | { 186 | Log.d(TAG, drawString); 187 | //File Manager: "content://org.openintents.cmfilemanager/mimetype//mnt/sdcard/DCIM/Camera/IMG_20110909_210412.jpg" 188 | //ASTRO: "file:///mnt/sdcard/DCIM/Camera/IMG_20110924_133324.jpg" 189 | if (drawString.contains("//")) 190 | { 191 | drawString = drawString.substring(drawString.lastIndexOf("//")); 192 | } 193 | } 194 | 195 | // set the background to the selected picture 196 | if (drawString.length() > 0) 197 | { 198 | Drawable drawBackground = Drawable.createFromPath(drawString); 199 | drawView.setBackgroundDrawable(drawBackground); 200 | } 201 | 202 | } 203 | } 204 | 205 | // used when trying to get an image path from the URI returned by the Gallery app 206 | public String getGalleryPath(Uri uri) { 207 | String[] projection = { MediaStore.Images.Media.DATA }; 208 | Cursor cursor = managedQuery(uri, projection, null, null, null); 209 | 210 | if (cursor != null) 211 | { 212 | int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); 213 | cursor.moveToFirst(); 214 | return cursor.getString(column_index); 215 | } 216 | 217 | 218 | return null; 219 | } 220 | 221 | } -------------------------------------------------------------------------------- /app/src/main/java/nt/finger/paint/FriendlyPoint.java: -------------------------------------------------------------------------------- 1 | package nt.finger.paint; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | 6 | public class FriendlyPoint extends Point { 7 | 8 | private final Point neighbour; 9 | 10 | public FriendlyPoint(final float x, final float y, final int col, final Point neighbour, final int width) { 11 | super(x, y, col, width); 12 | this.neighbour = neighbour; 13 | } 14 | 15 | @Override 16 | public void draw(final Canvas canvas, final Paint paint) { 17 | paint.setColor(col); 18 | paint.setStrokeWidth(width); 19 | canvas.drawLine(x, y, neighbour.x, neighbour.y, paint); 20 | // experimenting with smoother drawing -- 21 | // the circle should hopefully act as a smoothing hinge between line segments 22 | // o----o----o------o 23 | canvas.drawCircle(x, y, width/2, paint); 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return x + ", " + y + ", " + col + "; N[" + neighbour.toString() + "]"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/nt/finger/paint/Point.java: -------------------------------------------------------------------------------- 1 | package nt.finger.paint; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | 6 | public class Point { 7 | public final float x, y; 8 | public final int col; 9 | public final int width; 10 | 11 | public Point(final float x, final float y, final int col, final int width) { 12 | this.x = x; 13 | this.y = y; 14 | this.col = col; 15 | this.width = width; 16 | } 17 | 18 | public void draw(final Canvas canvas, final Paint paint) { 19 | paint.setColor(col); 20 | canvas.drawCircle(x, y, width/2, paint); 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return x + ", " + y + ", " + col; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/res/anim/translate_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/anim/translate_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angcyo/PaintDemo/0fc20dd32bf69a9188fb3216ea5b365a5838757a/app/src/main/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angcyo/PaintDemo/0fc20dd32bf69a9188fb3216ea5b365a5838757a/app/src/main/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angcyo/PaintDemo/0fc20dd32bf69a9188fb3216ea5b365a5838757a/app/src/main/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/popup_button_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 |