├── .idea
├── .name
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── vcs.xml
├── modules.xml
├── runConfigurations.xml
├── compiler.xml
├── gradle.xml
└── misc.xml
├── demo
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── styles.xml
│ │ │ ├── drawable
│ │ │ │ ├── earth.png
│ │ │ │ ├── stars.png
│ │ │ │ ├── basketball.png
│ │ │ │ ├── img_font_frame.9.png
│ │ │ │ ├── btn_sticker_turn_n.png
│ │ │ │ ├── btn_sticker_cancel_n.png
│ │ │ │ ├── btn_sticker_word_turn_n.png
│ │ │ │ └── shape_text_stroke.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-w820dp
│ │ │ │ └── dimens.xml
│ │ │ └── layout
│ │ │ │ ├── activity_text_vertical.xml
│ │ │ │ └── activity_main.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── addword
│ │ │ └── demo
│ │ │ ├── MyApplication.java
│ │ │ └── MainActivity.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── addword
│ │ │ └── demo
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── addword
│ │ └── demo
│ │ └── ApplicationTest.java
├── proguard-rules.pro
└── build.gradle
├── AddwordLib
├── .gitignore
├── matrix.gif
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── dimens.xml
│ │ │ ├── colors.xml
│ │ │ └── styles.xml
│ │ ├── drawable
│ │ │ ├── earth.png
│ │ │ ├── stars.png
│ │ │ ├── basketball.png
│ │ │ └── shape_text_stroke.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
│ │ ├── drawable-xhdpi
│ │ │ ├── img_font_frame.9.png
│ │ │ ├── btn_sticker_turn_n.png
│ │ │ ├── btn_sticker_cancel_n.png
│ │ │ └── btn_sticker_word_turn_n.png
│ │ └── values-w820dp
│ │ │ └── dimens.xml
│ │ ├── java
│ │ └── com
│ │ │ ├── funny
│ │ │ └── addworddemo
│ │ │ │ ├── AppConst.java
│ │ │ │ ├── util
│ │ │ │ ├── StringUtils.java
│ │ │ │ ├── StatusBarHeightUtil.java
│ │ │ │ ├── BitmapUtils.java
│ │ │ │ ├── DeviceInfoUtils.java
│ │ │ │ └── LogUtils.java
│ │ │ │ └── view
│ │ │ │ ├── AddFrameHolder.java
│ │ │ │ ├── AddWordFrameState.java
│ │ │ │ ├── AddWordTextView.java
│ │ │ │ ├── AddWordInsideLinearlayout.java
│ │ │ │ ├── AddWordFrame.java
│ │ │ │ └── AddWordOutsideLinearLayout.java
│ │ │ └── almeros
│ │ │ └── android
│ │ │ └── multitouch
│ │ │ ├── BaseGestureDetector.java
│ │ │ ├── RotateGestureDetector.java
│ │ │ ├── TwoFingerGestureDetector.java
│ │ │ ├── MoveGestureDetector.java
│ │ │ └── ShoveGestureDetector.java
│ │ └── AndroidManifest.xml
├── screenshots
│ └── textview横竖.gif
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── apk
└── demo.apk
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── README_CN.md
├── gradle.properties
├── gradlew.bat
├── gradlew
└── README.md
/.idea/.name:
--------------------------------------------------------------------------------
1 | AddwordDemo
--------------------------------------------------------------------------------
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/AddwordLib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':addwordLib', ':demo'
2 |
--------------------------------------------------------------------------------
/apk/demo.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/apk/demo.apk
--------------------------------------------------------------------------------
/AddwordLib/matrix.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/matrix.gif
--------------------------------------------------------------------------------
/demo/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Demo
3 |
4 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AddWordDemo
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/AddwordLib/screenshots/textview横竖.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/screenshots/textview横竖.gif
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/earth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/drawable/earth.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/stars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/drawable/stars.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/drawable/earth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/drawable/earth.png
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/drawable/stars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/drawable/stars.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/basketball.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/drawable/basketball.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/drawable/basketball.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/drawable/basketball.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/img_font_frame.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/drawable/img_font_frame.9.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/btn_sticker_turn_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/drawable/btn_sticker_turn_n.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/btn_sticker_cancel_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/drawable/btn_sticker_cancel_n.png
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/btn_sticker_word_turn_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/demo/src/main/res/drawable/btn_sticker_word_turn_n.png
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/drawable-xhdpi/img_font_frame.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/drawable-xhdpi/img_font_frame.9.png
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/drawable-xhdpi/btn_sticker_turn_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/drawable-xhdpi/btn_sticker_turn_n.png
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/drawable-xhdpi/btn_sticker_cancel_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/drawable-xhdpi/btn_sticker_cancel_n.png
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/drawable-xhdpi/btn_sticker_word_turn_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinguangyue/AddwordLib/HEAD/AddwordLib/src/main/res/drawable-xhdpi/btn_sticker_word_turn_n.png
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/AppConst.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo;
2 |
3 | /**
4 | * Created by yue on 2016/5/12.
5 | */
6 | public class AppConst {
7 | public static int textHeight = 0;
8 | }
9 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/util/StringUtils.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo.util;
2 |
3 | /**
4 | * Created by yue on 2016/4/14.
5 | */
6 | public class StringUtils {
7 | public static boolean isEnglish(String charaString) {
8 | return charaString.matches("^[a-zA-Z]*");
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #ffffff
7 | #000000
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/demo/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/demo/src/test/java/com/addword/demo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.addword.demo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/demo/src/androidTest/java/com/addword/demo/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.addword.demo;
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 | }
--------------------------------------------------------------------------------
/demo/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/shape_text_stroke.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/drawable/shape_text_stroke.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/util/StatusBarHeightUtil.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo.util;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * Created by yue on 2016/1/5.
7 | */
8 | public class StatusBarHeightUtil {
9 |
10 | public static int getStatusBarHeight(Context context) {
11 | int result = 0;
12 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
13 | if (resourceId > 0) {
14 | result = context.getResources().getDimensionPixelSize(resourceId);
15 | }
16 | return result;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/view/AddFrameHolder.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo.view;
2 |
3 | public class AddFrameHolder {
4 |
5 | private AddWordFrame addWordFrame;
6 | private AddWordFrameState state;
7 |
8 | public AddWordFrame getAddWordFrame() {
9 | return addWordFrame;
10 | }
11 |
12 | public void setAddWordFrame(AddWordFrame addWordFrame) {
13 | this.addWordFrame = addWordFrame;
14 | }
15 |
16 | public AddWordFrameState getState() {
17 | return state;
18 | }
19 |
20 | public void setState(AddWordFrameState state) {
21 | this.state = state;
22 | }
23 | }
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/util/BitmapUtils.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo.util;
2 |
3 | import android.graphics.Bitmap;
4 | import android.view.View;
5 |
6 | public class BitmapUtils {
7 |
8 | /**
9 | * @param view
10 | * @return
11 | */
12 | public static Bitmap convertViewToBitmap(View view) {
13 | view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
14 | view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
15 | view.buildDrawingCache();
16 | Bitmap bitmap = view.getDrawingCache();
17 | return bitmap;
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/demo/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:\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 |
--------------------------------------------------------------------------------
/AddwordLib/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:\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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/demo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/demo/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.addword.demo"
9 | minSdkVersion 16
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.3.0'
26 | compile project(':addwordLib')
27 | // compile 'com.jinguangyue.addwordlib:addwordLib:1.0.0'
28 | }
29 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/util/DeviceInfoUtils.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo.util;
2 |
3 | import android.content.Context;
4 | import android.util.DisplayMetrics;
5 |
6 | /**
7 | * Created by yue on 15/10/29.
8 | */
9 | @SuppressWarnings("deprecation")
10 | public class DeviceInfoUtils {
11 | /**
12 | * @param context
13 | * @return int
14 | */
15 | public static int getScreenHeight(Context context) {
16 | DisplayMetrics dm = context.getResources().getDisplayMetrics();
17 | return dm.heightPixels;
18 | }
19 |
20 | /**
21 | * @param context
22 | * @return int
23 | */
24 | public static int getScreenWidth(Context context) {
25 | DisplayMetrics dm = context.getResources().getDisplayMetrics();
26 | return dm.widthPixels;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | # AddwordDemo
2 | TextView加文字横竖排切换(字体方向不变)
3 |
4 | 一个普通的Textview如果旋转的话字体方向肯定变了,这里我自定义了一个view来实现这个功能。
5 |
6 | #实现的主要功能
7 | * 文字横竖排切换, 并且切换成竖排是文字方向不变
8 | * 单指移动, 缩放
9 | * 双指缩放旋转
10 | * 可以添加多个
11 | * 支持文字居左居中居右
12 | * 当你编辑文字时自动适应边框
13 |
14 | # 你可以在Android studio里面这样引入
15 | ```
16 | dependencies {
17 | compile 'com.jinguangyue.addwordlib:addwordLib:1.0.0'
18 | }
19 | ```
20 |
21 | **具体的代码可以查看Demo下面的MainActivity**
22 |
23 | # 效果图
24 | 
25 |
26 | 具体讲解可以点击我的博客:
27 | http://blog.csdn.net/coderyue/article/details/51149936
28 |
29 |
30 |
31 | ## 关注我的 Google Play 独立开发公众号
32 | ## 通过从零到一在 Google Play 开发出下载量 300万 APP 的经历,我将把很多精彩的故事分享到公众号,扫描下方二维码和我一起开发 APP 赚美元吧!
33 |
34 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/view/AddWordFrameState.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo.view;
2 |
3 | public class AddWordFrameState {
4 | private float left;
5 | private float top;
6 | private float right;
7 | private float bottom;
8 |
9 | @Override
10 | public String toString() {
11 | return "[" + left + "," + top + "," + right + "," + bottom + "] (" + (right - left) + ","
12 | + (bottom - top) + ")";// super.toString();
13 | }
14 |
15 | public float getLeft() {
16 | return left;
17 | }
18 |
19 | public void setLeft(float left) {
20 | this.left = left;
21 | }
22 |
23 | public float getTop() {
24 | return top;
25 | }
26 |
27 | public void setTop(float top) {
28 | this.top = top;
29 | }
30 |
31 | public float getRight() {
32 | return right;
33 | }
34 |
35 | public void setRight(float right) {
36 | this.right = right;
37 | }
38 |
39 | public float getBottom() {
40 | return bottom;
41 | }
42 |
43 | public void setBottom(float bottom) {
44 | this.bottom = bottom;
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/demo/src/main/java/com/addword/demo/MyApplication.java:
--------------------------------------------------------------------------------
1 | package com.addword.demo;
2 |
3 | import android.app.Application;
4 |
5 | import com.funny.addworddemo.AppConst;
6 | import com.funny.addworddemo.util.DeviceInfoUtils;
7 |
8 | /**
9 | * Created by yue on 2016/4/18.
10 | */
11 | public class MyApplication extends Application {
12 |
13 | private static MyApplication instance;
14 | private int textHeight;
15 | private int screenWidth;
16 | private int screenHeight;
17 |
18 | public static MyApplication getInstance() {
19 | return instance;
20 | }
21 |
22 | @Override
23 | public void onCreate() {
24 | super.onCreate();
25 | instance = this;
26 | screenWidth = DeviceInfoUtils.getScreenWidth(this);//获取屏幕宽度
27 | screenHeight = DeviceInfoUtils.getScreenHeight(this);//获取屏幕高度
28 | }
29 |
30 | public int getTextHeight() {
31 | if(textHeight == 0){
32 | textHeight = AppConst.textHeight;
33 | }
34 | return textHeight;
35 | }
36 |
37 | public void setTextHeight(int textHeight) {
38 | this.textHeight = textHeight;
39 | }
40 |
41 | public int getScreenWidth() {
42 | return screenWidth;
43 | }
44 |
45 | public int getScreenHeight() {
46 | return screenHeight;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/view/AddWordTextView.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.text.TextPaint;
8 | import android.util.AttributeSet;
9 | import android.widget.TextView;
10 |
11 | /**
12 | * Created by yue on 2016/4/28.
13 | */
14 | public class AddWordTextView extends TextView {
15 | private int borderColor = Color.TRANSPARENT;
16 | private int textColor = Color.WHITE;
17 | private boolean isStroke = false;
18 |
19 | public AddWordTextView(Context context) {
20 | super(context);
21 | }
22 |
23 | public AddWordTextView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public AddWordTextView(Context context, AttributeSet attrs, int defStyleAttr) {
28 | super(context, attrs, defStyleAttr);
29 | }
30 |
31 | public boolean isStroke() {
32 | return isStroke;
33 | }
34 |
35 | public void setStroke(boolean stroke) {
36 | isStroke = stroke;
37 | }
38 |
39 | public void setBorderColor(int borderColor, int textColor, boolean isStroke) {
40 | this.borderColor = borderColor;
41 | this.textColor = textColor;
42 | this.isStroke = isStroke;
43 | invalidate();
44 | }
45 |
46 | public int getBorderColor() {
47 | return borderColor;
48 | }
49 |
50 | @Override
51 | protected void onDraw(Canvas canvas) {
52 | if(isStroke){
53 | this.setTextColor(borderColor);
54 | TextPaint tp = this.getPaint();
55 | tp.setFakeBoldText(true);
56 | tp.setStrokeWidth(5);
57 | tp.setStyle(Paint.Style.STROKE);
58 | tp.setColor(borderColor);
59 | super.onDraw(canvas);
60 |
61 | tp.setColor(textColor);
62 | tp.setStyle(Paint.Style.STROKE);
63 | this.setTextColor(textColor);
64 | super.onDraw(canvas);
65 | }else{
66 | this.setTextColor(textColor);
67 | TextPaint tp = this.getPaint();
68 | tp.setFakeBoldText(false);
69 | tp.setStrokeWidth(0);
70 | super.onDraw(canvas);
71 | }
72 |
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_text_vertical.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
14 |
19 |
20 |
25 |
26 |
31 |
32 |
37 |
38 |
43 |
44 |
45 |
51 |
52 |
57 |
58 |
63 |
64 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
14 |
15 |
20 |
21 |
26 |
27 |
32 |
33 |
38 |
39 |
44 |
45 |
46 |
52 |
53 |
58 |
59 |
64 |
65 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/util/LogUtils.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo.util;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | /**
7 | * Created by Wxcily on 15/10/29.
8 | */
9 | public class LogUtils {
10 | private static String TAG = "Funny";
11 | private static boolean DEBUG = true;
12 | private static boolean SHOW_MORE_INFO = false;
13 |
14 | private LogUtils() {
15 | }
16 |
17 | public static void init(Context context, String tag, boolean debug) {
18 | TAG = tag;
19 | DEBUG = debug;
20 | }
21 |
22 | public static String getTAG() {
23 | return TAG;
24 | }
25 |
26 | public static void setTAG(String TAG) {
27 | LogUtils.TAG = TAG;
28 | }
29 |
30 | public static boolean isDEBUG() {
31 | return DEBUG;
32 | }
33 |
34 | public static void setDEBUG(boolean DEBUG) {
35 | LogUtils.DEBUG = DEBUG;
36 | }
37 |
38 | public static boolean isShowMoreInfo() {
39 | return SHOW_MORE_INFO;
40 | }
41 |
42 | public static void setShowMoreInfo(boolean showMoreInfo) {
43 | SHOW_MORE_INFO = showMoreInfo;
44 | }
45 |
46 | public static void v(String tag, String msg) {
47 | if (DEBUG) {
48 | Log.v(tag, msg);
49 | }
50 | }
51 |
52 | public static void d(String tag, String msg) {
53 | if (DEBUG) {
54 | Log.d(tag, msg);
55 | }
56 | }
57 |
58 | public static void i(String tag, String msg) {
59 | if (DEBUG) {
60 | Log.i(tag, msg);
61 | }
62 | }
63 |
64 | public static void w(String tag, String msg) {
65 | if (DEBUG) {
66 | Log.w(tag, msg);
67 | }
68 | }
69 |
70 | public static void e(String tag, String msg) {
71 | if (DEBUG) {
72 | Log.e(tag, msg);
73 | }
74 | }
75 |
76 | public static void v(String msg) {
77 | if (DEBUG) {
78 | Log.v(TAG, msg);
79 | }
80 | }
81 |
82 | public static void d(String msg) {
83 | if (DEBUG) {
84 | Log.d(TAG, msg);
85 | }
86 | }
87 |
88 | public static void i(String msg) {
89 | if (DEBUG) {
90 | Log.i(TAG, msg);
91 | }
92 | }
93 |
94 | public static void w(String msg) {
95 | if (DEBUG) {
96 | Log.w(TAG, msg);
97 | }
98 | }
99 |
100 | public static void e(String msg) {
101 | if (DEBUG) {
102 | Log.e(TAG, msg);
103 | }
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.7
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/AddwordLib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven' //you add
3 | apply plugin: 'com.jfrog.bintray' //you add
4 |
5 | version = "1.0.0" //your version
6 |
7 | android {
8 | compileSdkVersion 23
9 | buildToolsVersion "23.0.2"
10 |
11 | defaultConfig {
12 | minSdkVersion 16
13 | targetSdkVersion 23
14 | versionCode 1
15 | versionName "1.0.0"
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | dependencies {
26 | compile fileTree(dir: 'libs', include: ['*.jar'])
27 | testCompile 'junit:junit:4.12'
28 | compile 'com.android.support:appcompat-v7:23.1.1'
29 |
30 | }
31 |
32 | def siteUrl = 'https://github.com/jinguangyue/AddwordLib' // project homepage
33 | def gitUrl = 'https://github.com/jinguangyue/AddwordLib.git' // project git
34 | group = "com.jinguangyue.addwordlib"
35 |
36 | install {
37 | repositories.mavenInstaller {
38 | // This generates POM.xml with proper parameters
39 | pom {
40 | project {
41 | packaging 'aar'
42 | name 'This is the vertical TextView lib For Android'
43 | url siteUrl
44 | licenses {
45 | license {
46 | name 'The Apache Software License, Version 2.0'
47 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
48 | }
49 | }
50 | developers {
51 | developer {
52 | id 'jinguangyue'
53 | name 'jinguangyue'
54 | email '1010398694@qq.com'
55 | }
56 | }
57 | scm {
58 | connection gitUrl
59 | developerConnection gitUrl
60 | url siteUrl
61 | }
62 | }
63 | }
64 | }
65 | }
66 |
67 | task sourcesJar(type: Jar) {
68 | from android.sourceSets.main.java.srcDirs
69 | classifier = 'sources'
70 | }
71 | task javadoc(type: Javadoc) {
72 | source = android.sourceSets.main.java.srcDirs
73 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
74 | }
75 | task javadocJar(type: Jar, dependsOn: javadoc) {
76 | classifier = 'javadoc'
77 | from javadoc.destinationDir
78 | }
79 | artifacts {
80 | archives javadocJar
81 | archives sourcesJar
82 | }
83 |
84 | Properties properties = new Properties()
85 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
86 | bintray {
87 | user = properties.getProperty("bintray.user")
88 | key = properties.getProperty("bintray.apikey")
89 | configurations = ['archives']
90 | pkg {
91 | repo = "maven"
92 | name = "addwordlib"
93 | websiteUrl = siteUrl
94 | vcsUrl = gitUrl
95 | licenses = ["Apache-2.0"]
96 | publish = true
97 | }
98 | }
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/view/AddWordInsideLinearlayout.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo.view;
2 |
3 | import android.content.Context;
4 | import android.os.Build;
5 | import android.text.TextUtils;
6 | import android.util.AttributeSet;
7 | import android.view.Gravity;
8 | import android.view.ViewTreeObserver;
9 | import android.widget.LinearLayout;
10 | import android.widget.TextView;
11 |
12 | import com.funny.addworddemo.AppConst;
13 | import com.funny.addworddemo.util.LogUtils;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | /**
19 | * Created by yue on 2016/4/13.
20 | */
21 | public class AddWordInsideLinearlayout extends LinearLayout {
22 | private String text;
23 | private Context context;
24 | private int color;
25 | private int size;
26 | private List textViews;
27 | private AddWordTextView myText;
28 |
29 | public AddWordInsideLinearlayout(Context context) {
30 | super(context);
31 | setTextViewOrientation(VERTICAL);
32 | this.context = context;
33 | this.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
34 | textViews = new ArrayList();
35 | }
36 |
37 | public AddWordInsideLinearlayout(Context context, AttributeSet attrs) {
38 | super(context, attrs);
39 | setTextViewOrientation(VERTICAL);
40 | this.context = context;
41 | this.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
42 | textViews = new ArrayList();
43 | }
44 |
45 | public void setTextViewOrientation(int orientation) {
46 | setOrientation(orientation);
47 | }
48 |
49 | public void setText(String text) {
50 | this.text = text;
51 | addText();
52 | }
53 |
54 | private void addText() {
55 | removeAllViews();
56 | textViews.clear();
57 |
58 | if (!TextUtils.isEmpty(text)) {
59 | char[] chars = text.toCharArray();
60 | for (int i = 0; i < chars.length; i++) {
61 | myText = new AddWordTextView(context);
62 | myText.setTextColor(color);
63 | if (size > 0) {
64 | myText.setTextSize(size);
65 | }
66 | myText.setText(text.substring(i, i + 1));
67 | // myText.setIncludeFontPadding(false);
68 | textViews.add(myText);
69 | myText.setGravity(Gravity.CENTER);
70 | addView(myText);
71 | }
72 |
73 | myText.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
74 | @Override
75 | public void onGlobalLayout() {
76 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
77 | myText.getViewTreeObserver().removeOnGlobalLayoutListener(this);
78 | }
79 |
80 | if(myText.getHeight() != 0){
81 | AppConst.textHeight = myText.getHeight();
82 | }
83 | AppConst.textHeight = myText.getHeight();
84 | }
85 | });
86 | }
87 | }
88 |
89 | public List getTextViews() {
90 | return textViews;
91 | }
92 |
93 | public void setTextViews(List textViews) {
94 | this.textViews = textViews;
95 | }
96 |
97 | public void setTextColor(int color) {
98 | this.color = color;
99 | }
100 |
101 | public void setTextSize(int size) {
102 | this.size = size;
103 | }
104 |
105 | public int getSize() {
106 | return size;
107 | }
108 |
109 | public int getColor() {
110 | return color;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/almeros/android/multitouch/BaseGestureDetector.java:
--------------------------------------------------------------------------------
1 | package com.almeros.android.multitouch;
2 |
3 | import android.content.Context;
4 | import android.view.MotionEvent;
5 |
6 | /**
7 | * @author Almer Thie (code.almeros.com)
8 | * Copyright (c) 2013, Almer Thie (code.almeros.com)
9 | *
10 | * All rights reserved.
11 | *
12 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
13 | *
14 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
15 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
16 | * in the documentation and/or other materials provided with the distribution.
17 | *
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
22 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
24 | * OF SUCH DAMAGE.
25 | */
26 | public abstract class BaseGestureDetector {
27 | protected final Context mContext;
28 | protected boolean mGestureInProgress;
29 |
30 | protected MotionEvent mPrevEvent;
31 | protected MotionEvent mCurrEvent;
32 |
33 | protected float mCurrPressure;
34 | protected float mPrevPressure;
35 | protected long mTimeDelta;
36 |
37 |
38 | /**
39 | * This value is the threshold ratio between the previous combined pressure
40 | * and the current combined pressure. When pressure decreases rapidly
41 | * between events the position values can often be imprecise, as it usually
42 | * indicates that the user is in the process of lifting a pointer off of the
43 | * device. This value was tuned experimentally.
44 | */
45 | protected static final float PRESSURE_THRESHOLD = 0.67f;
46 |
47 |
48 | public BaseGestureDetector(Context context) {
49 | mContext = context;
50 | }
51 |
52 | /**
53 | * All gesture detectors need to be called through this method to be able to
54 | * detect gestures. This method delegates work to handler methods
55 | * (handleStartProgressEvent, handleInProgressEvent) implemented in
56 | * extending classes.
57 | *
58 | * @param event
59 | * @return
60 | */
61 | public boolean onTouchEvent(MotionEvent event){
62 | final int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
63 | if (!mGestureInProgress) {
64 | handleStartProgressEvent(actionCode, event);
65 | } else {
66 | handleInProgressEvent(actionCode, event);
67 | }
68 | return true;
69 | }
70 |
71 | /**
72 | * Called when the current event occurred when NO gesture is in progress
73 | * yet. The handling in this implementation may set the gesture in progress
74 | * (via mGestureInProgress) or out of progress
75 | * @param actionCode
76 | * @param event
77 | */
78 | protected abstract void handleStartProgressEvent(int actionCode, MotionEvent event);
79 |
80 | /**
81 | * Called when the current event occurred when a gesture IS in progress. The
82 | * handling in this implementation may set the gesture out of progress (via
83 | * mGestureInProgress).
84 | * @param actionCode
85 | * @param event
86 | */
87 | protected abstract void handleInProgressEvent(int actionCode, MotionEvent event);
88 |
89 |
90 | protected void updateStateByEvent(MotionEvent curr){
91 | final MotionEvent prev = mPrevEvent;
92 |
93 | // Reset mCurrEvent
94 | if (mCurrEvent != null) {
95 | mCurrEvent.recycle();
96 | mCurrEvent = null;
97 | }
98 | mCurrEvent = MotionEvent.obtain(curr);
99 |
100 |
101 | // Delta time
102 | mTimeDelta = curr.getEventTime() - prev.getEventTime();
103 |
104 | // Pressure
105 | mCurrPressure = curr.getPressure(curr.getActionIndex());
106 | mPrevPressure = prev.getPressure(prev.getActionIndex());
107 | }
108 |
109 | protected void resetState() {
110 | if (mPrevEvent != null) {
111 | mPrevEvent.recycle();
112 | mPrevEvent = null;
113 | }
114 | if (mCurrEvent != null) {
115 | mCurrEvent.recycle();
116 | mCurrEvent = null;
117 | }
118 | mGestureInProgress = false;
119 | }
120 |
121 |
122 | /**
123 | * Returns {@code true} if a gesture is currently in progress.
124 | * @return {@code true} if a gesture is currently in progress, {@code false} otherwise.
125 | */
126 | public boolean isInProgress() {
127 | return mGestureInProgress;
128 | }
129 |
130 | /**
131 | * Return the time difference in milliseconds between the previous accepted
132 | * GestureDetector event and the current GestureDetector event.
133 | *
134 | * @return Time difference since the last move event in milliseconds.
135 | */
136 | public long getTimeDelta() {
137 | return mTimeDelta;
138 | }
139 |
140 | /**
141 | * Return the event time of the current GestureDetector event being
142 | * processed.
143 | *
144 | * @return Current GestureDetector event time in milliseconds.
145 | */
146 | public long getEventTime() {
147 | return mCurrEvent.getEventTime();
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AddwordLib([中文文档](https://github.com/jinguangyue/AddwordLib/blob/master/README_CN.md))
2 | TextView add text , vertical discharge switching (the direction of the font is no change!)
3 |
4 | If an ordinary Textview font rotation direction ,
5 | then direction must be changed, but here I customized a view to achieve this function .
6 |
7 | #Features([DownLoad APK](https://github.com/jinguangyue/AddwordLib/blob/master/apk/demo.apk?raw=true))
8 |
9 | * The Text , horizontal, and vertical text direction unchanged when switching to
10 | * can move one finger , one finger zoom
11 | * Two-finger rotating zoom
12 | * add multiple
13 | * Support the text centered Left Right
14 | * Automatically adapt to the border when entering text
15 |
16 | #Include library
17 |
18 | Edit your build.gradle file and add below dependency:
19 |
20 | ```
21 | dependencies {
22 | compile 'com.jinguangyue.addwordlib:addwordLib:1.0.0'
23 | }
24 | ```
25 |
26 | # Screenshots
27 | 
28 |
29 |
30 | # Use it
31 |
32 | **Add View**
33 | Here you can add more than one view, and you can move and resize. You Can do this for Add view:
34 |
35 | ```
36 | private void addMyFrame() {
37 | //Each additional view Now set all view is not selected
38 | for (int i = (addFrameHolders.size() - 1); i >= 0; i--) {
39 | AddWordFrame addWordFrame = addFrameHolders.get(i).getAddWordFrame();
40 | if (addWordFrame.isSelect()) {
41 | addWordFrame.setSelect(false);
42 | break;
43 | }
44 | }
45 |
46 | //new a view
47 | addWordFrame = new AddWordFrame(this);
48 | addWordFrame.setSelect(true);
49 | //add to your frame
50 | frame.addView(addWordFrame);
51 |
52 | layout = addWordFrame.getLayout();
53 |
54 | addWordBitmap = BitmapUtils.convertViewToBitmap(layout);
55 |
56 | addWordWidth = addWordBitmap.getWidth();
57 | addWordHeight = addWordBitmap.getHeight();
58 |
59 | //Set to the center of the screen and set the vertical and horizontal coordinates of four points
60 | addWordx1 = width/2 - addWordWidth /2;
61 | addWordy1 = height/3;
62 | addWordFrame.leftTop.set(addWordx1, addWordy1);
63 | addWordFrame.rightTop.set(addWordx1 + addWordWidth, addWordy1);
64 | addWordFrame.leftBottom.set(addWordx1, addWordy1 + addWordHeight);
65 | addWordFrame.rightBottom.set(addWordx1 + addWordWidth, addWordy1 + addWordHeight);
66 |
67 |
68 | //here use matrix to scaling gesture
69 | addWordMatrix = new Matrix();
70 | addWordMatrix.postTranslate(addWordx1, addWordy1);
71 | addWordFrame.setMatrix(addWordMatrix);
72 |
73 | //Here for each view with a rectangular package , click the rectangle on selected current view
74 | AddWordFrameState addWordFrameState = new AddWordFrameState();
75 | addWordFrameState.setLeft(addWordx1);
76 | addWordFrameState.setTop(addWordy1);
77 | addWordFrameState.setRight(addWordx1 + addWordWidth);
78 | addWordFrameState.setBottom(addWordy1 + addWordHeight);
79 |
80 | AddFrameHolder addFrameHolder = new AddFrameHolder();
81 | addFrameHolder.setAddWordFrame(addWordFrame);
82 | addFrameHolder.setState(addWordFrameState);
83 | addFrameHolders.add(addFrameHolder);
84 |
85 | addWordFrame.setOnTouchListener(new AddWordMyOntouch());
86 | AddWordSelectImageCount = addFrameHolders.size() - 1;
87 | }
88 | ```
89 |
90 | **Control select which View**
91 |
92 | ```
93 | private void selectMyFrame(float x, float y) {
94 | //Select the option to cancel all back to only one click is selected
95 | for (int i = (addFrameHolders.size() - 1); i >= 0; i--) {
96 | AddFrameHolder addFrameHolder = addFrameHolders.get(i);
97 | if (addFrameHolder.getAddWordFrame().isSelect()) {
98 | addFrameHolder.getAddWordFrame().setSelect(false);
99 | break;
100 | }
101 | }
102 |
103 | for (int i = (addFrameHolders.size() - 1); i >= 0; i--) {
104 | AddFrameHolder addFrameHolder = addFrameHolders.get(i);
105 | //Create a rectangular area here getLeft getTop etc. mean the current view of the leftmost
106 | // uppermost and lowermost rightmost only to click inside the region is selected
107 | Rect rect = new Rect((int)addFrameHolder.getState().getLeft(),
108 | (int)addFrameHolder.getState().getTop(),
109 | (int)addFrameHolder.getState().getRight(),
110 | (int)addFrameHolder.getState().getBottom());
111 |
112 | if (rect.contains((int) x, (int) y)) {
113 | //If you select the current view mentioned uppermost layer
114 | addFrameHolder.getAddWordFrame().bringToFront();
115 | addFrameHolder.getAddWordFrame().setSelect(true);
116 | //Which record is selected
117 | AddWordSelectImageCount = i;
118 | LogUtils.e("selected");
119 | break;
120 | }
121 | AddWordSelectImageCount = -1;
122 | LogUtils.e("no select");
123 | }
124 | }
125 | ```
126 |
127 | **Demo detailed code can be viewed below MainActivity**
128 |
129 | ## 关注我的 Google Play 独立开发公众号
130 | ## 通过从零到一在 Google Play 开发出下载量 300万 APP 的经历,我将把很多精彩的故事分享到公众号,扫描下方二维码和我一起开发 APP 赚美元吧!
131 |
132 |
133 | #License
134 | ```
135 | Copyright 2016 jinguangyue
136 |
137 | Licensed under the Apache License, Version 2.0 (the "License");
138 | you may not use this file except in compliance with the License.
139 | You may obtain a copy of the License at
140 |
141 | http://www.apache.org/licenses/LICENSE-2.0
142 |
143 | Unless required by applicable law or agreed to in writing, software
144 | distributed under the License is distributed on an "AS IS" BASIS,
145 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
146 | See the License for the specific language governing permissions and
147 | limitations under the License.
148 | ```
149 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/almeros/android/multitouch/RotateGestureDetector.java:
--------------------------------------------------------------------------------
1 | package com.almeros.android.multitouch;
2 |
3 | import android.content.Context;
4 | import android.view.MotionEvent;
5 |
6 | /**
7 | * @author Almer Thie (code.almeros.com)
8 | * Copyright (c) 2013, Almer Thie (code.almeros.com)
9 | *
10 | * All rights reserved.
11 | *
12 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
13 | *
14 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
15 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
16 | * in the documentation and/or other materials provided with the distribution.
17 | *
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
22 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
24 | * OF SUCH DAMAGE.
25 | */
26 | public class RotateGestureDetector extends TwoFingerGestureDetector {
27 |
28 | /**
29 | * Listener which must be implemented which is used by RotateGestureDetector
30 | * to perform callbacks to any implementing class which is registered to a
31 | * RotateGestureDetector via the constructor.
32 | *
33 | * @see SimpleOnRotateGestureListener
34 | */
35 | public interface OnRotateGestureListener {
36 | public boolean onRotate(RotateGestureDetector detector);
37 | public boolean onRotateBegin(RotateGestureDetector detector);
38 | public void onRotateEnd(RotateGestureDetector detector);
39 | }
40 |
41 | /**
42 | * Helper class which may be extended and where the methods may be
43 | * implemented. This way it is not necessary to implement all methods
44 | * of OnRotateGestureListener.
45 | */
46 | public static class SimpleOnRotateGestureListener implements OnRotateGestureListener {
47 | public boolean onRotate(RotateGestureDetector detector) {
48 | return false;
49 | }
50 |
51 | public boolean onRotateBegin(RotateGestureDetector detector) {
52 | return true;
53 | }
54 |
55 | public void onRotateEnd(RotateGestureDetector detector) {
56 | // Do nothing, overridden implementation may be used
57 | }
58 | }
59 |
60 |
61 | private final OnRotateGestureListener mListener;
62 | private boolean mSloppyGesture;
63 |
64 | public RotateGestureDetector(Context context, OnRotateGestureListener listener) {
65 | super(context);
66 | mListener = listener;
67 | }
68 |
69 | @Override
70 | protected void handleStartProgressEvent(int actionCode, MotionEvent event){
71 | switch (actionCode) {
72 | case MotionEvent.ACTION_POINTER_DOWN:
73 | // At least the second finger is on screen now
74 |
75 | resetState(); // In case we missed an UP/CANCEL event
76 | mPrevEvent = MotionEvent.obtain(event);
77 | mTimeDelta = 0;
78 |
79 | updateStateByEvent(event);
80 |
81 | // See if we have a sloppy gesture
82 | mSloppyGesture = isSloppyGesture(event);
83 | if(!mSloppyGesture){
84 | // No, start gesture now
85 | mGestureInProgress = mListener.onRotateBegin(this);
86 | }
87 | break;
88 |
89 | case MotionEvent.ACTION_MOVE:
90 | if (!mSloppyGesture) {
91 | break;
92 | }
93 |
94 | // See if we still have a sloppy gesture
95 | mSloppyGesture = isSloppyGesture(event);
96 | if(!mSloppyGesture){
97 | // No, start normal gesture now
98 | mGestureInProgress = mListener.onRotateBegin(this);
99 | }
100 |
101 | break;
102 |
103 | case MotionEvent.ACTION_POINTER_UP:
104 | if (!mSloppyGesture) {
105 | break;
106 | }
107 |
108 | break;
109 | }
110 | }
111 |
112 |
113 | @Override
114 | protected void handleInProgressEvent(int actionCode, MotionEvent event){
115 | switch (actionCode) {
116 | case MotionEvent.ACTION_POINTER_UP:
117 | // Gesture ended but
118 | updateStateByEvent(event);
119 |
120 | if (!mSloppyGesture) {
121 | mListener.onRotateEnd(this);
122 | }
123 |
124 | resetState();
125 | break;
126 |
127 | case MotionEvent.ACTION_CANCEL:
128 | if (!mSloppyGesture) {
129 | mListener.onRotateEnd(this);
130 | }
131 |
132 | resetState();
133 | break;
134 |
135 | case MotionEvent.ACTION_MOVE:
136 | updateStateByEvent(event);
137 |
138 | // Only accept the event if our relative pressure is within
139 | // a certain limit. This can help filter shaky data as a
140 | // finger is lifted.
141 | if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
142 | final boolean updatePrevious = mListener.onRotate(this);
143 | if (updatePrevious) {
144 | mPrevEvent.recycle();
145 | mPrevEvent = MotionEvent.obtain(event);
146 | }
147 | }
148 | break;
149 | }
150 | }
151 |
152 | @Override
153 | protected void resetState() {
154 | super.resetState();
155 | mSloppyGesture = false;
156 | }
157 |
158 |
159 | /**
160 | * Return the rotation difference from the previous rotate event to the current
161 | * event.
162 | *
163 | * @return The current rotation //difference in degrees.
164 | */
165 | public float getRotationDegreesDelta() {
166 | double diffRadians = Math.atan2(mPrevFingerDiffY, mPrevFingerDiffX) - Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX);
167 | return (float) (diffRadians * 180 / Math.PI);
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/almeros/android/multitouch/TwoFingerGestureDetector.java:
--------------------------------------------------------------------------------
1 | package com.almeros.android.multitouch;
2 |
3 | import android.content.Context;
4 | import android.util.DisplayMetrics;
5 | import android.view.MotionEvent;
6 | import android.view.ViewConfiguration;
7 |
8 | /**
9 | * @author Almer Thie (code.almeros.com)
10 | * Copyright (c) 2013, Almer Thie (code.almeros.com)
11 | *
12 | * All rights reserved.
13 | *
14 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
15 | *
16 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
17 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
18 | * in the documentation and/or other materials provided with the distribution.
19 | *
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
21 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
26 | * OF SUCH DAMAGE.
27 | */
28 | public abstract class TwoFingerGestureDetector extends BaseGestureDetector {
29 |
30 | private final float mEdgeSlop;
31 | private float mRightSlopEdge;
32 | private float mBottomSlopEdge;
33 |
34 | protected float mPrevFingerDiffX;
35 | protected float mPrevFingerDiffY;
36 | protected float mCurrFingerDiffX;
37 | protected float mCurrFingerDiffY;
38 |
39 | private float mCurrLen;
40 | private float mPrevLen;
41 |
42 | public TwoFingerGestureDetector(Context context) {
43 | super(context);
44 |
45 | ViewConfiguration config = ViewConfiguration.get(context);
46 | mEdgeSlop = config.getScaledEdgeSlop();
47 | }
48 |
49 | @Override
50 | protected abstract void handleStartProgressEvent(int actionCode, MotionEvent event);
51 |
52 | @Override
53 | protected abstract void handleInProgressEvent(int actionCode, MotionEvent event);
54 |
55 | protected void updateStateByEvent(MotionEvent curr){
56 | super.updateStateByEvent(curr);
57 |
58 | final MotionEvent prev = mPrevEvent;
59 |
60 | mCurrLen = -1;
61 | mPrevLen = -1;
62 |
63 | // Previous
64 | final float px0 = prev.getX(0);
65 | final float py0 = prev.getY(0);
66 | final float px1 = prev.getX(1);
67 | final float py1 = prev.getY(1);
68 | final float pvx = px1 - px0;
69 | final float pvy = py1 - py0;
70 | mPrevFingerDiffX = pvx;
71 | mPrevFingerDiffY = pvy;
72 |
73 | // Current
74 | final float cx0 = curr.getX(0);
75 | final float cy0 = curr.getY(0);
76 | final float cx1 = curr.getX(1);
77 | final float cy1 = curr.getY(1);
78 | final float cvx = cx1 - cx0;
79 | final float cvy = cy1 - cy0;
80 | mCurrFingerDiffX = cvx;
81 | mCurrFingerDiffY = cvy;
82 | }
83 |
84 | /**
85 | * Return the current distance between the two pointers forming the
86 | * gesture in progress.
87 | *
88 | * @return Distance between pointers in pixels.
89 | */
90 | public float getCurrentSpan() {
91 | if (mCurrLen == -1) {
92 | final float cvx = mCurrFingerDiffX;
93 | final float cvy = mCurrFingerDiffY;
94 | mCurrLen = (float) Math.sqrt(cvx*cvx + cvy*cvy);
95 | }
96 | return mCurrLen;
97 | }
98 |
99 | /**
100 | * Return the previous distance between the two pointers forming the
101 | * gesture in progress.
102 | *
103 | * @return Previous distance between pointers in pixels.
104 | */
105 | public float getPreviousSpan() {
106 | if (mPrevLen == -1) {
107 | final float pvx = mPrevFingerDiffX;
108 | final float pvy = mPrevFingerDiffY;
109 | mPrevLen = (float) Math.sqrt(pvx*pvx + pvy*pvy);
110 | }
111 | return mPrevLen;
112 | }
113 |
114 | /**
115 | * MotionEvent has no getRawX(int) method; simulate it pending future API approval.
116 | * @param event
117 | * @param pointerIndex
118 | * @return
119 | */
120 | protected static float getRawX(MotionEvent event, int pointerIndex) {
121 | float offset = event.getX() - event.getRawX();
122 | if(pointerIndex < event.getPointerCount()){
123 | return event.getX(pointerIndex) + offset;
124 | }
125 | return 0f;
126 | }
127 |
128 | /**
129 | * MotionEvent has no getRawY(int) method; simulate it pending future API approval.
130 | * @param event
131 | * @param pointerIndex
132 | * @return
133 | */
134 | protected static float getRawY(MotionEvent event, int pointerIndex) {
135 | float offset = event.getY() - event.getRawY();
136 | if(pointerIndex < event.getPointerCount()){
137 | return event.getY(pointerIndex) + offset;
138 | }
139 | return 0f;
140 | }
141 |
142 | /**
143 | * Check if we have a sloppy gesture. Sloppy gestures can happen if the edge
144 | * of the user's hand is touching the screen, for example.
145 | *
146 | * @param event
147 | * @return
148 | */
149 | protected boolean isSloppyGesture(MotionEvent event){
150 | // As orientation can change, query the metrics in touch down
151 | DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
152 | mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
153 | mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
154 |
155 | final float edgeSlop = mEdgeSlop;
156 | final float rightSlop = mRightSlopEdge;
157 | final float bottomSlop = mBottomSlopEdge;
158 |
159 | final float x0 = event.getRawX();
160 | final float y0 = event.getRawY();
161 | final float x1 = getRawX(event, 1);
162 | final float y1 = getRawY(event, 1);
163 |
164 | boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
165 | || x0 > rightSlop || y0 > bottomSlop;
166 | boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
167 | || x1 > rightSlop || y1 > bottomSlop;
168 |
169 | if (p0sloppy && p1sloppy) {
170 | return true;
171 | } else if (p0sloppy) {
172 | return true;
173 | } else if (p1sloppy) {
174 | return true;
175 | }
176 | return false;
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/almeros/android/multitouch/MoveGestureDetector.java:
--------------------------------------------------------------------------------
1 | package com.almeros.android.multitouch;
2 |
3 | import android.content.Context;
4 | import android.graphics.PointF;
5 | import android.view.MotionEvent;
6 | import com.almeros.android.multitouch.BaseGestureDetector;
7 |
8 | /**
9 | * @author Almer Thie (code.almeros.com)
10 | * Copyright (c) 2013, Almer Thie (code.almeros.com)
11 | *
12 | * All rights reserved.
13 | *
14 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
15 | *
16 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
17 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
18 | * in the documentation and/or other materials provided with the distribution.
19 | *
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
21 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
26 | * OF SUCH DAMAGE.
27 | */
28 | public class MoveGestureDetector extends BaseGestureDetector {
29 |
30 | /**
31 | * Listener which must be implemented which is used by MoveGestureDetector
32 | * to perform callbacks to any implementing class which is registered to a
33 | * MoveGestureDetector via the constructor.
34 | *
35 | * @see SimpleOnMoveGestureListener
36 | */
37 | public interface OnMoveGestureListener {
38 | public boolean onMove(MoveGestureDetector detector);
39 | public boolean onMoveBegin(MoveGestureDetector detector);
40 | public void onMoveEnd(MoveGestureDetector detector);
41 | }
42 |
43 | /**
44 | * Helper class which may be extended and where the methods may be
45 | * implemented. This way it is not necessary to implement all methods
46 | * of OnMoveGestureListener.
47 | */
48 | public static class SimpleOnMoveGestureListener implements OnMoveGestureListener {
49 | public boolean onMove(MoveGestureDetector detector) {
50 | return false;
51 | }
52 |
53 | public boolean onMoveBegin(MoveGestureDetector detector) {
54 | return true;
55 | }
56 |
57 | public void onMoveEnd(MoveGestureDetector detector) {
58 | // Do nothing, overridden implementation may be used
59 | }
60 | }
61 |
62 | private static final PointF FOCUS_DELTA_ZERO = new PointF();
63 |
64 | private final OnMoveGestureListener mListener;
65 |
66 | private PointF mCurrFocusInternal;
67 | private PointF mPrevFocusInternal;
68 | private PointF mFocusExternal = new PointF();
69 | private PointF mFocusDeltaExternal = new PointF();
70 |
71 |
72 | public MoveGestureDetector(Context context, OnMoveGestureListener listener) {
73 | super(context);
74 | mListener = listener;
75 | }
76 |
77 | @Override
78 | protected void handleStartProgressEvent(int actionCode, MotionEvent event){
79 | switch (actionCode) {
80 | case MotionEvent.ACTION_DOWN:
81 | resetState(); // In case we missed an UP/CANCEL event
82 |
83 | mPrevEvent = MotionEvent.obtain(event);
84 | mTimeDelta = 0;
85 |
86 | updateStateByEvent(event);
87 | break;
88 |
89 | case MotionEvent.ACTION_MOVE:
90 | mGestureInProgress = mListener.onMoveBegin(this);
91 | break;
92 | }
93 | }
94 |
95 | @Override
96 | protected void handleInProgressEvent(int actionCode, MotionEvent event){
97 | switch (actionCode) {
98 | case MotionEvent.ACTION_UP:
99 | case MotionEvent.ACTION_CANCEL:
100 | mListener.onMoveEnd(this);
101 | resetState();
102 | break;
103 |
104 | case MotionEvent.ACTION_MOVE:
105 | updateStateByEvent(event);
106 |
107 | // Only accept the event if our relative pressure is within
108 | // a certain limit. This can help filter shaky data as a
109 | // finger is lifted.
110 | if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
111 | final boolean updatePrevious = mListener.onMove(this);
112 | if (updatePrevious) {
113 | mPrevEvent.recycle();
114 | mPrevEvent = MotionEvent.obtain(event);
115 | }
116 | }
117 | break;
118 | }
119 | }
120 |
121 | protected void updateStateByEvent(MotionEvent curr) {
122 | super.updateStateByEvent(curr);
123 |
124 | final MotionEvent prev = mPrevEvent;
125 |
126 | // Focus intenal
127 | mCurrFocusInternal = determineFocalPoint(curr);
128 | mPrevFocusInternal = determineFocalPoint(prev);
129 |
130 | // Focus external
131 | // - Prevent skipping of focus delta when a finger is added or removed
132 | boolean mSkipNextMoveEvent = prev.getPointerCount() != curr.getPointerCount();
133 | mFocusDeltaExternal = mSkipNextMoveEvent ? FOCUS_DELTA_ZERO : new PointF(mCurrFocusInternal.x - mPrevFocusInternal.x, mCurrFocusInternal.y - mPrevFocusInternal.y);
134 |
135 | // - Don't directly use mFocusInternal (or skipping will occur). Add
136 | // unskipped delta values to mFocusExternal instead.
137 | mFocusExternal.x += mFocusDeltaExternal.x;
138 | mFocusExternal.y += mFocusDeltaExternal.y;
139 | }
140 |
141 | /**
142 | * Determine (multi)finger focal point (a.k.a. center point between all
143 | * fingers)
144 | *
145 | * @param MotionEvent e
146 | * @return PointF focal point
147 | */
148 | private PointF determineFocalPoint(MotionEvent e){
149 | // Number of fingers on screen
150 | final int pCount = e.getPointerCount();
151 | float x = 0f;
152 | float y = 0f;
153 |
154 | for(int i = 0; i < pCount; i++){
155 | x += e.getX(i);
156 | y += e.getY(i);
157 | }
158 |
159 | return new PointF(x/pCount, y/pCount);
160 | }
161 |
162 | public float getFocusX() {
163 | return mFocusExternal.x;
164 | }
165 |
166 | public float getFocusY() {
167 | return mFocusExternal.y;
168 | }
169 |
170 | public PointF getFocusDelta() {
171 | return mFocusDeltaExternal;
172 | }
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/almeros/android/multitouch/ShoveGestureDetector.java:
--------------------------------------------------------------------------------
1 | package com.almeros.android.multitouch;
2 |
3 | import android.content.Context;
4 | import android.view.MotionEvent;
5 |
6 | /**
7 | * @author Robert Nordan (robert.nordan@norkart.no)
8 | *
9 | * Copyright (c) 2013, Norkart AS
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
14 | *
15 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
16 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
17 | * in the documentation and/or other materials provided with the distribution.
18 | *
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
25 | * OF SUCH DAMAGE.
26 | */
27 | public class ShoveGestureDetector extends TwoFingerGestureDetector {
28 |
29 | /**
30 | * Listener which must be implemented which is used by ShoveGestureDetector
31 | * to perform callbacks to any implementing class which is registered to a
32 | * ShoveGestureDetector via the constructor.
33 | *
34 | * @see SimpleOnShoveGestureListener
35 | */
36 | public interface OnShoveGestureListener {
37 | public boolean onShove(ShoveGestureDetector detector);
38 | public boolean onShoveBegin(ShoveGestureDetector detector);
39 | public void onShoveEnd(ShoveGestureDetector detector);
40 | }
41 |
42 | /**
43 | * Helper class which may be extended and where the methods may be
44 | * implemented. This way it is not necessary to implement all methods
45 | * of OnShoveGestureListener.
46 | */
47 | public static class SimpleOnShoveGestureListener implements OnShoveGestureListener {
48 | public boolean onShove(ShoveGestureDetector detector) {
49 | return false;
50 | }
51 |
52 | public boolean onShoveBegin(ShoveGestureDetector detector) {
53 | return true;
54 | }
55 |
56 | public void onShoveEnd(ShoveGestureDetector detector) {
57 | // Do nothing, overridden implementation may be used
58 | }
59 | }
60 |
61 | private float mPrevAverageY;
62 | private float mCurrAverageY;
63 |
64 | private final OnShoveGestureListener mListener;
65 | private boolean mSloppyGesture;
66 |
67 | public ShoveGestureDetector(Context context, OnShoveGestureListener listener) {
68 | super(context);
69 | mListener = listener;
70 | }
71 |
72 | @Override
73 | protected void handleStartProgressEvent(int actionCode, MotionEvent event){
74 | switch (actionCode) {
75 | case MotionEvent.ACTION_POINTER_DOWN:
76 | // At least the second finger is on screen now
77 |
78 | resetState(); // In case we missed an UP/CANCEL event
79 | mPrevEvent = MotionEvent.obtain(event);
80 | mTimeDelta = 0;
81 |
82 | updateStateByEvent(event);
83 |
84 | // See if we have a sloppy gesture
85 | mSloppyGesture = isSloppyGesture(event);
86 | if(!mSloppyGesture){
87 | // No, start gesture now
88 | mGestureInProgress = mListener.onShoveBegin(this);
89 | }
90 | break;
91 |
92 | case MotionEvent.ACTION_MOVE:
93 | if (!mSloppyGesture) {
94 | break;
95 | }
96 |
97 | // See if we still have a sloppy gesture
98 | mSloppyGesture = isSloppyGesture(event);
99 | if(!mSloppyGesture){
100 | // No, start normal gesture now
101 | mGestureInProgress = mListener.onShoveBegin(this);
102 | }
103 |
104 | break;
105 |
106 | case MotionEvent.ACTION_POINTER_UP:
107 | if (!mSloppyGesture) {
108 | break;
109 | }
110 |
111 | break;
112 | }
113 | }
114 |
115 |
116 | @Override
117 | protected void handleInProgressEvent(int actionCode, MotionEvent event){
118 | switch (actionCode) {
119 | case MotionEvent.ACTION_POINTER_UP:
120 | // Gesture ended but
121 | updateStateByEvent(event);
122 |
123 | if (!mSloppyGesture) {
124 | mListener.onShoveEnd(this);
125 | }
126 |
127 | resetState();
128 | break;
129 |
130 | case MotionEvent.ACTION_CANCEL:
131 | if (!mSloppyGesture) {
132 | mListener.onShoveEnd(this);
133 | }
134 |
135 | resetState();
136 | break;
137 |
138 | case MotionEvent.ACTION_MOVE:
139 | updateStateByEvent(event);
140 |
141 | // Only accept the event if our relative pressure is within
142 | // a certain limit. This can help filter shaky data as a
143 | // finger is lifted. Also check that shove is meaningful.
144 | if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD
145 | && Math.abs(getShovePixelsDelta()) > 0.5f) {
146 | final boolean updatePrevious = mListener.onShove(this);
147 | if (updatePrevious) {
148 | mPrevEvent.recycle();
149 | mPrevEvent = MotionEvent.obtain(event);
150 | }
151 | }
152 | break;
153 | }
154 | }
155 |
156 | @Override
157 | protected void resetState() {
158 | super.resetState();
159 | mSloppyGesture = false;
160 | mPrevAverageY = 0.0f;
161 | mCurrAverageY = 0.0f;
162 | }
163 |
164 | @Override
165 | protected void updateStateByEvent(MotionEvent curr){
166 | super.updateStateByEvent(curr);
167 |
168 | final MotionEvent prev = mPrevEvent;
169 | float py0 = prev.getY(0);
170 | float py1 = prev.getY(1);
171 | mPrevAverageY = (py0 + py1) / 2.0f;
172 |
173 | float cy0 = curr.getY(0);
174 | float cy1 = curr.getY(1);
175 | mCurrAverageY = (cy0 + cy1) / 2.0f;
176 | }
177 |
178 | @Override
179 | protected boolean isSloppyGesture(MotionEvent event){
180 | boolean sloppy = super.isSloppyGesture(event);
181 | if (sloppy)
182 | return true;
183 |
184 | // If it's not traditionally sloppy, we check if the angle between fingers
185 | // is acceptable.
186 | double angle = Math.abs(Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX));
187 | //about 20 degrees, left or right
188 | return !(( 0.0f < angle && angle < 0.35f)
189 | || 2.79f < angle && angle < Math.PI);
190 | }
191 |
192 |
193 | /**
194 | * Return the distance in pixels from the previous shove event to the current
195 | * event.
196 | *
197 | * @return The current distance in pixels.
198 | */
199 | public float getShovePixelsDelta() {
200 | return mCurrAverageY - mPrevAverageY;
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/view/AddWordFrame.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Matrix;
9 | import android.graphics.Paint;
10 | import android.graphics.PointF;
11 | import android.os.Handler;
12 | import android.util.AttributeSet;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.widget.FrameLayout;
16 | import android.widget.LinearLayout;
17 |
18 | import com.funny.addworddemo.R;
19 | import com.funny.addworddemo.util.BitmapUtils;
20 | import com.funny.addworddemo.util.LogUtils;
21 |
22 | /**
23 | * Created by yue on 2016/4/22.
24 | */
25 | public class AddWordFrame extends FrameLayout {
26 | private Paint paint;
27 | private int mImageHeight = 50, mImageWidth = 200;
28 |
29 | private Bitmap bitmap;
30 |
31 | private Context context;
32 | private AddWordOutsideLinearLayout layout;
33 |
34 | private float sx;
35 | private float sy;
36 | private float dx;
37 | private float dy;
38 | private float degrees;
39 | private float px;
40 | private float py;
41 | private float centerX;
42 | private float centerY;
43 |
44 | public PointF leftTop = new PointF();
45 | public PointF rightTop = new PointF();
46 | public PointF leftBottom = new PointF();
47 | public PointF rightBottom = new PointF();
48 | private boolean isSelect = false;
49 | public Bitmap bitDelete = null;
50 | public Bitmap bitMove = null;
51 |
52 | Matrix matrix = new Matrix();
53 |
54 | private int bdeleteWidth;
55 | private boolean isHeng = true;
56 | private String allText;
57 | private int xiao;
58 |
59 | public AddWordFrame(Context context) {
60 | super(context);
61 | this.context = context;
62 | initPaint();
63 | addWordView();
64 | setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
65 | setWillNotDraw(false);
66 | }
67 |
68 | public AddWordFrame(Context context, AttributeSet attrs) {
69 | super(context, attrs);
70 | this.context = context;
71 | initPaint();
72 | addWordView();
73 | setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
74 | setWillNotDraw(false);
75 | }
76 |
77 | public AddWordFrame(Context context, AttributeSet attrs, int defStyleAttr) {
78 | super(context, attrs, defStyleAttr);
79 | this.context = context;
80 | initPaint();
81 | addWordView();
82 | setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
83 | setWillNotDraw(false);
84 | }
85 |
86 | public AddWordOutsideLinearLayout getLayout() {
87 | return layout;
88 | }
89 |
90 | public String getAllText() {
91 | return allText;
92 | }
93 |
94 | public void setAllText(String allText) {
95 | this.allText = allText;
96 | }
97 |
98 | private void addWordView() {
99 | layout = new AddWordOutsideLinearLayout(context);
100 | layout.setTextColor(Color.WHITE);
101 | layout.setTextSize(30);
102 | layout.setTextViewOrientation(LinearLayout.VERTICAL);
103 | layout.setText("Double Click");
104 | bitmap = BitmapUtils.convertViewToBitmap(layout);
105 |
106 | mImageWidth = bitmap.getWidth();
107 | mImageHeight = bitmap.getHeight();
108 |
109 | LogUtils.i("mImageWidth===" + mImageWidth);
110 | LogUtils.i("mImageHeight===" + mImageHeight);
111 |
112 | layout.setmImageWidth(mImageWidth);
113 | layout.setmImageHeight(mImageHeight);
114 |
115 | layout.setSelect(true);
116 | // layout.setBackgroundResource(R.drawable.img_font_frame);
117 | addView(layout);
118 | }
119 |
120 | public int getmImageHeight() {
121 | return mImageHeight;
122 | }
123 |
124 | public void setmImageHeight(int mImageHeight) {
125 | this.mImageHeight = mImageHeight;
126 | }
127 |
128 | public int getmImageWidth() {
129 | return mImageWidth;
130 | }
131 |
132 | public void setmImageWidth(int mImageWidth) {
133 | this.mImageWidth = mImageWidth;
134 | }
135 |
136 | public void postScale(float sx, float sy, float centerX, float centerY) {
137 | this.sx = sx;
138 | this.sy = sy;
139 | this.centerX = centerX;
140 | this.centerY = centerY;
141 | }
142 |
143 | public void postTranslate(float dx, float dy) {
144 | this.dx = dx;
145 | this.dy = dy;
146 | }
147 |
148 | public void postRotate(float degrees, float px, float py) {
149 | this.degrees = degrees;
150 | this.px = px;
151 | this.py = py;
152 | }
153 |
154 | public boolean isHeng() {
155 | return isHeng;
156 | }
157 |
158 | public void setHeng(boolean heng) {
159 | isHeng = heng;
160 | invalidate();
161 | }
162 |
163 | private void initPaint() {
164 | paint = new Paint();
165 | paint.setAntiAlias(true);
166 |
167 | paint.setColor(Color.WHITE);
168 | paint.setStrokeWidth(5f);
169 |
170 | if (bitDelete == null) {
171 | bitDelete = BitmapFactory.decodeResource(getResources(),
172 | R.drawable.btn_sticker_cancel_n);
173 | }
174 | if (bitMove == null) {
175 | bitMove = BitmapFactory.decodeResource(getResources(),
176 | R.drawable.btn_sticker_turn_n);
177 | }
178 | /*
179 |
180 | if (bgBitmap == null) {
181 | bgBitmap = BitmapFactory.decodeResource(getResources(),
182 | R.drawable.img_font_frame);
183 | }
184 | */
185 |
186 | xiao = bitDelete.getHeight()/2;
187 | // mSetfil = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG);
188 | }
189 |
190 | @Override
191 | protected void onDraw(Canvas canvas) {
192 | super.onDraw(canvas);
193 | setVisibility(INVISIBLE);
194 |
195 | if (isSelect) {
196 | canvas.drawLine(leftTop.x, leftTop.y, rightTop.x, rightTop.y, paint);
197 | canvas.drawLine(leftBottom.x, leftBottom.y, rightBottom.x,rightBottom.y, paint);
198 | canvas.drawLine(leftTop.x, leftTop.y, leftBottom.x, leftBottom.y,paint);
199 | canvas.drawLine(rightTop.x, rightTop.y, rightBottom.x,rightBottom.y, paint);
200 | canvas.drawBitmap(bitDelete, leftTop.x-xiao, leftTop.y-xiao, paint);
201 | canvas.drawBitmap(bitMove, rightBottom.x-xiao, rightBottom.y-xiao, paint);
202 | }
203 |
204 | this.setLayerType(View.LAYER_TYPE_HARDWARE, null);
205 | canvas.concat(matrix);
206 | this.setLayerType(View.LAYER_TYPE_NONE, null);
207 | setVisibility(VISIBLE);
208 | }
209 |
210 | @Override
211 | public Matrix getMatrix() {
212 | return matrix;
213 | }
214 |
215 | public boolean isSelect() {
216 | return isSelect;
217 | }
218 |
219 | public void setSelect(boolean select) {
220 | isSelect = select;
221 | if(select == false){
222 | // this.getLayout().setBackgroundResource(0);
223 | /*Intent intent = new Intent(AppConstant.ACTION.POPVIEWADDWORD_CLOSE);
224 | context.sendBroadcast(intent);*/
225 | }else{
226 | // this.getLayout().setBackgroundResource(R.drawable.img_font_frame);
227 | /*Intent intent = new Intent(AppConstant.ACTION.POPVIEWADDWORD_SHOW);
228 | context.sendBroadcast(intent);*/
229 | }
230 | // invalidate();
231 | }
232 |
233 | public void setMatrix(Matrix matrix) {
234 | this.matrix = matrix;
235 | final Handler handler = new Handler();
236 | handler.post(new Runnable() {
237 | @Override
238 | public void run() {
239 | postInvalidate();
240 | }
241 | });
242 | }
243 |
244 | }
245 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/addword/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.addword.demo;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Matrix;
7 | import android.graphics.PointF;
8 | import android.graphics.Rect;
9 | import android.os.Build;
10 | import android.os.Bundle;
11 | import android.text.Editable;
12 | import android.text.TextWatcher;
13 | import android.view.Gravity;
14 | import android.view.MotionEvent;
15 | import android.view.View;
16 | import android.widget.EditText;
17 | import android.widget.FrameLayout;
18 | import android.widget.LinearLayout;
19 |
20 | import com.funny.addworddemo.util.BitmapUtils;
21 | import com.funny.addworddemo.util.LogUtils;
22 | import com.funny.addworddemo.util.StatusBarHeightUtil;
23 | import com.funny.addworddemo.view.AddFrameHolder;
24 | import com.funny.addworddemo.view.AddWordFrame;
25 | import com.funny.addworddemo.view.AddWordFrameState;
26 | import com.funny.addworddemo.view.AddWordOutsideLinearLayout;
27 |
28 | import java.util.ArrayList;
29 | import java.util.List;
30 |
31 | public class MainActivity extends Activity implements View.OnClickListener {
32 | private EditText edit_text;
33 | private FrameLayout frame;
34 | private int width;
35 | private int height;
36 |
37 | private AddWordOutsideLinearLayout layout;
38 | private AddWordFrame addWordFrame;
39 | private int AddWordMode;
40 | private Bitmap addWordBitmap;
41 | private int addWordWidth;
42 | private int addWordHeight;
43 | private int addWordx1;
44 | private int addWordy1;
45 | private Matrix addWordMatrix = new Matrix();
46 | private Matrix addWordSavedMatrix = new Matrix();
47 | private List addFrameHolders;
48 | private int AddWordSelectImageCount = -1;
49 | private Context context;
50 |
51 | @Override
52 | protected void onCreate(Bundle savedInstanceState) {
53 | super.onCreate(savedInstanceState);
54 | context = this;
55 | setContentView(R.layout.activity_main);
56 | width = MyApplication.getInstance().getScreenWidth();
57 | height = MyApplication.getInstance().getScreenHeight();
58 | initView();
59 | initData();
60 | }
61 |
62 | private void initData() {
63 | addFrameHolders = new ArrayList<>();
64 | addMyFrame();
65 | }
66 |
67 | private void initView() {
68 | frame = (FrameLayout) findViewById(R.id.frame);
69 | findViewById(R.id.hengButton).setOnClickListener(this);
70 | findViewById(R.id.shuButton).setOnClickListener(this);
71 | findViewById(R.id.zuoduiqi).setOnClickListener(this);
72 | findViewById(R.id.juzhong).setOnClickListener(this);
73 | findViewById(R.id.youduiqi).setOnClickListener(this);
74 | findViewById(R.id.addView).setOnClickListener(this);
75 | edit_text = (EditText) findViewById(R.id.edit_text);
76 | edit_text.addTextChangedListener(new TextWatcher() {
77 | @Override
78 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
79 |
80 | }
81 |
82 | @Override
83 | public void onTextChanged(CharSequence s, int start, int before, int count) {
84 | addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout().setText(s.toString());
85 | ajustAddWord();
86 | }
87 |
88 | @Override
89 | public void afterTextChanged(Editable s) {
90 |
91 | }
92 | });
93 |
94 | }
95 |
96 |
97 | /**
98 | * 调整加字框的大小 以及删除和变换坐标
99 | */
100 | private void ajustAddWord(){
101 | if(AddWordSelectImageCount != -1){
102 | addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout().layoutWidthAndHeight(addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout(), new AddWordOutsideLinearLayout.OnLayoutWidth() {
103 | @Override
104 | public void layout(int width, int height) {
105 | addWordMatrix = addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getMatrix();
106 | addWordSavedMatrix.set(addWordMatrix);
107 | adjustLocation(addWordMatrix, addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame());
108 | }
109 | });
110 | }
111 | }
112 |
113 | @Override
114 | public void onClick(View v) {
115 | switch (v.getId()) {
116 | case R.id.hengButton:
117 | final AddWordFrame addWordFrame_heng = addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame();
118 | addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout().setTextViewOrientation(LinearLayout.VERTICAL);
119 |
120 | addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout().layoutWidthAndHeight(addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout(),new AddWordOutsideLinearLayout.OnLayoutWidth() {
121 | @Override
122 | public void layout(int width, int height) {
123 | addWordMatrix = addWordFrame_heng.getMatrix();
124 | adjustLocation(addWordMatrix, addWordFrame_heng);
125 | }
126 | });
127 | break;
128 |
129 | case R.id.shuButton:
130 | final AddWordFrame addWordFrame_shu = addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame();
131 | addWordFrame_shu.getLayout().setTextViewOrientation(LinearLayout.HORIZONTAL);
132 |
133 | addWordFrame_shu.getLayout().layoutWidthAndHeight(addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout(),
134 | new AddWordOutsideLinearLayout.OnLayoutWidth() {
135 | @Override
136 | public void layout(int width, int height) {
137 | addWordMatrix = addWordFrame_shu.getMatrix();
138 | adjustLocation(addWordMatrix, addWordFrame_shu);
139 | }
140 | });
141 | break;
142 |
143 | case R.id.zuoduiqi:
144 | if (addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout().getOrientation() == LinearLayout.HORIZONTAL) {
145 | addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout().setMyGravity(Gravity.TOP);
146 | } else {
147 | addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout().setMyGravity(Gravity.LEFT);
148 | }
149 |
150 | break;
151 |
152 | case R.id.juzhong:
153 | addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout().setMyGravity(Gravity.CENTER);
154 | break;
155 |
156 | case R.id.youduiqi:
157 | if (addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout().getOrientation() == LinearLayout.HORIZONTAL) {
158 | addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout().setMyGravity(Gravity.BOTTOM);
159 | } else {
160 | addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getLayout().setMyGravity(Gravity.RIGHT);
161 | }
162 | break;
163 |
164 | case R.id.addView:
165 | addMyFrame();
166 | break;
167 | }
168 | }
169 |
170 | /**
171 | * 平移1个单位 只为调整位置
172 | * @param matrix
173 | * @param addWordFrame
174 | */
175 | private void adjustLocation(Matrix matrix, AddWordFrame addWordFrame){
176 | //将有缩放平移和旋转相关值的矩阵赋值到f中
177 | float[] f = new float[9];
178 | matrix.getValues(f);
179 | int bWidth = 0;
180 | int bHeight = 0;
181 |
182 | //取到view的宽高
183 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
184 | Rect rect = new Rect();
185 | addWordFrame.getLayout().getGlobalVisibleRect(rect);
186 | bWidth = rect.width();
187 | bHeight = rect.height();
188 | } else {
189 | bWidth = addWordWidth;
190 | bHeight = addWordHeight;
191 | }
192 |
193 | //如果想知道这里这样设置值的具体算法那必须要了解9*9的矩阵每个坐标的含义了 有兴趣的可以查阅一下 资料很多
194 | // 原图左上角
195 | float x1 = f[2];
196 | float y1 = f[5];
197 | addWordFrame.leftTop.set(x1, y1);
198 | // 原图右上角
199 | float x2 = f[0] * bWidth + f[2];
200 | float y2 = f[3] * bWidth + f[5];
201 | addWordFrame.rightTop.set(x2, y2);
202 | // 原图左下角
203 | float x3 = f[1] * bHeight + f[2];
204 | float y3 = f[4] * bHeight + f[5];
205 | addWordFrame.leftBottom.set(x3, y3);
206 | // 原图右下角
207 | float x4 = f[0] * bWidth + f[1] * bHeight + f[2];
208 | float y4 = f[3] * bWidth + f[4] * bHeight + f[5];
209 | addWordFrame.rightBottom.set(x4, y4);
210 |
211 |
212 | //这里一定要这是图片的最左 最右 最上 和 最下的位置 用来判断是不是点击到了当前的view
213 | // 最左边x
214 | float minX = 0;
215 | // 最右边x
216 | float maxX = 0;
217 | // 最上边y
218 | float minY = 0;
219 | // 最下边y
220 | float maxY = 0;
221 |
222 | minX = Math.min(x4, Math.min(x3, Math.min(x1, x2))) - 30;
223 | maxX = Math.max(x4, Math.max(x3, Math.max(x1, x2))) + 30;
224 | minY = Math.min(y4, Math.min(y3, Math.min(y1, y2))) - 30;
225 | maxY = Math.max(y4, Math.max(y3, Math.max(y1, y2))) + 30;
226 |
227 | addFrameHolders.get(AddWordSelectImageCount).getState().setLeft(minX);
228 | addFrameHolders.get(AddWordSelectImageCount).getState().setTop(minY);
229 | addFrameHolders.get(AddWordSelectImageCount).getState().setRight(maxX);
230 | addFrameHolders.get(AddWordSelectImageCount).getState().setBottom(maxY);
231 |
232 | //将当前的view设置上矩阵对象
233 | addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().setMatrix(matrix);
234 | }
235 |
236 | private void addMyFrame() {
237 | //Each additional view Now set all view is not selected
238 | for (int i = (addFrameHolders.size() - 1); i >= 0; i--) {
239 | AddWordFrame addWordFrame = addFrameHolders.get(i).getAddWordFrame();
240 | if (addWordFrame.isSelect()) {
241 | addWordFrame.setSelect(false);
242 | break;
243 | }
244 | }
245 |
246 | //new a view
247 | addWordFrame = new AddWordFrame(this);
248 | addWordFrame.setSelect(true);
249 | //add to your frame
250 | frame.addView(addWordFrame);
251 |
252 | layout = addWordFrame.getLayout();
253 |
254 | addWordBitmap = BitmapUtils.convertViewToBitmap(layout);
255 |
256 | addWordWidth = addWordBitmap.getWidth();
257 | addWordHeight = addWordBitmap.getHeight();
258 |
259 | //Set to the center of the screen and set the vertical and horizontal coordinates of four points
260 | addWordx1 = width/2 - addWordWidth /2;
261 | addWordy1 = height/3;
262 | addWordFrame.leftTop.set(addWordx1, addWordy1);
263 | addWordFrame.rightTop.set(addWordx1 + addWordWidth, addWordy1);
264 | addWordFrame.leftBottom.set(addWordx1, addWordy1 + addWordHeight);
265 | addWordFrame.rightBottom.set(addWordx1 + addWordWidth, addWordy1 + addWordHeight);
266 |
267 |
268 | //here use matrix to scaling gesture
269 | addWordMatrix = new Matrix();
270 | addWordMatrix.postTranslate(addWordx1, addWordy1);
271 | addWordFrame.setMatrix(addWordMatrix);
272 |
273 | //Here for each view with a rectangular package , click the rectangle on selected current view
274 | AddWordFrameState addWordFrameState = new AddWordFrameState();
275 | addWordFrameState.setLeft(addWordx1);
276 | addWordFrameState.setTop(addWordy1);
277 | addWordFrameState.setRight(addWordx1 + addWordWidth);
278 | addWordFrameState.setBottom(addWordy1 + addWordHeight);
279 |
280 | AddFrameHolder addFrameHolder = new AddFrameHolder();
281 | addFrameHolder.setAddWordFrame(addWordFrame);
282 | addFrameHolder.setState(addWordFrameState);
283 | addFrameHolders.add(addFrameHolder);
284 |
285 | addWordFrame.setOnTouchListener(new AddWordMyOntouch());
286 | AddWordSelectImageCount = addFrameHolders.size() - 1;
287 | }
288 |
289 | private void selectMyFrame(float x, float y) {
290 | //Select the option to cancel all back to only one click is selected
291 | for (int i = (addFrameHolders.size() - 1); i >= 0; i--) {
292 | AddFrameHolder addFrameHolder = addFrameHolders.get(i);
293 | if (addFrameHolder.getAddWordFrame().isSelect()) {
294 | addFrameHolder.getAddWordFrame().setSelect(false);
295 | break;
296 | }
297 | }
298 |
299 | for (int i = (addFrameHolders.size() - 1); i >= 0; i--) {
300 | AddFrameHolder addFrameHolder = addFrameHolders.get(i);
301 | //Create a rectangular area here getLeft getTop etc. mean the current view of the leftmost
302 | // uppermost and lowermost rightmost only to click inside the region is selected
303 | Rect rect = new Rect((int)addFrameHolder.getState().getLeft(),
304 | (int)addFrameHolder.getState().getTop(),
305 | (int)addFrameHolder.getState().getRight(),
306 | (int)addFrameHolder.getState().getBottom());
307 |
308 | if (rect.contains((int) x, (int) y)) {
309 | //If you select the current view mentioned uppermost layer
310 | addFrameHolder.getAddWordFrame().bringToFront();
311 | addFrameHolder.getAddWordFrame().setSelect(true);
312 | //Which record is selected
313 | AddWordSelectImageCount = i;
314 | LogUtils.e("selected");
315 | break;
316 | }
317 | AddWordSelectImageCount = -1;
318 | LogUtils.e("no select");
319 | }
320 | }
321 |
322 | class AddWordMyOntouch implements View.OnTouchListener {
323 | private float baseValue = 0;
324 | //The original angle
325 | private float oldRotation;
326 | //旋转和缩放的中点
327 | private PointF midP;
328 | //点中的要进行缩放的点与图片中点的距离
329 | private float imgLengHalf;
330 | //保存刚开始按下的点
331 | private PointF startPoinF = new PointF();
332 |
333 | private int NONE = 0; // 无
334 | private int DRAG = 1; // 移动
335 | private int ZOOM = 2; // 变换
336 | private int DOUBLE_ZOOM = 3;
337 |
338 | @Override
339 | public boolean onTouch(View v, MotionEvent event) {
340 | int eventaction = event.getAction();
341 | float event_x = (int) event.getRawX();
342 | float event_y = (int) event.getRawY() - StatusBarHeightUtil.getStatusBarHeight(context);
343 |
344 | //这里算是一个点击区域值 点中删除或者点中变换的100 * 100 的矩形区域 用这个区域来判断是否点中
345 | int tempInt = 50;
346 | int addint = 50;
347 |
348 | switch (eventaction & MotionEvent.ACTION_MASK) {
349 | case MotionEvent.ACTION_DOWN: // touch down so check if the
350 |
351 | baseValue = 0;
352 |
353 | startPoinF.set(event_x, event_y);// 保存刚开始按下的坐标
354 |
355 | //因为可能要添加多个这样的view 所以要按选中了哪个
356 | selectMyFrame(event_x, event_y);
357 |
358 | //如果有选中状态的额view
359 | if (AddWordSelectImageCount != -1) {
360 | addWordFrame = addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame();
361 | addWordMatrix = addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame().getMatrix();
362 | addWordSavedMatrix.set(addWordMatrix);
363 | AddWordMode = DRAG;
364 |
365 | //构造一个旋转按钮的矩形区域
366 | Rect moveRect = new Rect((int) addWordFrame.rightBottom.x - tempInt,
367 | (int) addWordFrame.rightBottom.y - tempInt, (int) addWordFrame.rightBottom.x + addint,
368 | (int) addWordFrame.rightBottom.y + addint);
369 | //删除按钮的矩形区域
370 | Rect deleteRect = new Rect((int) addWordFrame.leftTop.x - tempInt,
371 | (int) addWordFrame.leftTop.y - tempInt, (int) addWordFrame.leftTop.x + addint,
372 | (int) addWordFrame.leftTop.y + addint);
373 |
374 |
375 | //如果点中了变换
376 | if(moveRect.contains((int)event_x, (int)event_y)){
377 | LogUtils.e("点中了变换");
378 | // 点中了变换
379 | midP = midPoint(addWordFrame.leftTop, addWordFrame.rightBottom);
380 | imgLengHalf = spacing(midP, addWordFrame.rightBottom);
381 | oldRotation = rotation(midP, startPoinF);
382 | AddWordMode = ZOOM;
383 | }else if (deleteRect.contains((int)event_x, (int)event_y)) {
384 | // 点中了删除
385 | LogUtils.e("点中了删除");
386 | deleteMyFrame();
387 | }
388 | }
389 | break;
390 |
391 | case MotionEvent.ACTION_POINTER_DOWN:
392 | AddWordMode = NONE;
393 | if(AddWordSelectImageCount != -1){
394 | midP = midPoint(addWordFrame.leftTop, addWordFrame.rightBottom);
395 | imgLengHalf = spacing(midP, addWordFrame.rightBottom);
396 | oldRotation = rotationforTwo(event);
397 | }
398 | break;
399 |
400 | case MotionEvent.ACTION_MOVE: // touch drag with the ball
401 | //如果是双指点中
402 | if (event.getPointerCount() == 2) {
403 | if (AddWordSelectImageCount != -1) {
404 | AddWordMode = DOUBLE_ZOOM;
405 | float x = event.getX(0) - event.getX(1);
406 | float y = event.getY(0) - event.getY(1);
407 | float value = (float) Math.sqrt(x * x + y * y);// 计算两点的距离
408 |
409 | //旋转的角度
410 | float newRotation = rotationforTwo(event) - oldRotation;
411 | if (baseValue == 0) {
412 | baseValue = value;
413 | } else {
414 | //旋转到一定角度再执行 不能刚点击就执行旋转或者缩放
415 | if (value - baseValue >= 15 || value - baseValue <= -15) {
416 | float scale = value / baseValue;// 当前两点间的距离除以手指落下时两点间的距离就是需要缩放的比例。
417 | addWordMatrix.set(addWordSavedMatrix);
418 | addWordMatrix.postScale(scale, scale, midP.x, midP.y);
419 | addWordMatrix.postRotate(newRotation, midP.x, midP.y);
420 | }
421 | }
422 | }
423 | } else if (event.getPointerCount() == 1) {
424 | //单指点击
425 | if (AddWordSelectImageCount != -1) {
426 | if (AddWordMode == DRAG) {
427 | if (event_x < MyApplication.getInstance().getScreenWidth() - 50 && event_x > 50
428 | && event_y > 100
429 | && event_y < MyApplication.getInstance().getScreenHeight() - 100) {
430 | addWordMatrix.set(addWordSavedMatrix);
431 | // 图片移动的距离
432 | float translateX = event_x - startPoinF.x;
433 | float translateY = event_y - startPoinF.y;
434 | addWordMatrix.postTranslate(translateX, translateY);
435 | }
436 | } else if (AddWordMode == ZOOM) {
437 | //点击到了缩放旋转按钮
438 | PointF movePoin = new PointF(event_x, event_y);
439 |
440 | float moveLeng = spacing(startPoinF, movePoin);
441 | float newRotation = rotation(midP, movePoin) - oldRotation;
442 |
443 | if (moveLeng > 10f) {
444 | float moveToMidLeng = spacing(midP, movePoin);
445 | float scale = moveToMidLeng / imgLengHalf;
446 |
447 | addWordMatrix.set(addWordSavedMatrix);
448 | addWordMatrix.postScale(scale, scale, midP.x, midP.y);
449 | addWordMatrix.postRotate(newRotation, midP.x, midP.y);
450 | }
451 | }
452 | }
453 | }
454 |
455 | if(AddWordMode != NONE){
456 | if (AddWordSelectImageCount != -1) {
457 | //最后在action_move 执行完前设置好矩阵 设置view的位置
458 | addWordFrame = addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame();
459 | adjustLocation(addWordMatrix, addWordFrame);
460 | }
461 | }
462 | break;
463 |
464 | case MotionEvent.ACTION_POINTER_UP: //一只手指离开屏幕,但还有一只手指在上面会触此事件
465 | //什么都没做
466 | AddWordMode = NONE;
467 | break;
468 |
469 | case MotionEvent.ACTION_UP:
470 | AddWordMode = NONE;
471 | break;
472 | }
473 | return true;
474 | }
475 | }
476 |
477 | private void deleteMyFrame() {
478 | if (AddWordSelectImageCount != -1) {
479 | frame.removeView(addFrameHolders.get(AddWordSelectImageCount).getAddWordFrame());
480 | addFrameHolders.remove(AddWordSelectImageCount);
481 | AddWordSelectImageCount = -1;
482 | }
483 | }
484 |
485 | private float rotationforTwo(MotionEvent event) {
486 | double delta_x = (event.getX(0) - event.getX(1));
487 | double delta_y = (event.getY(0) - event.getY(1));
488 | double radians = Math.atan2(delta_y, delta_x);
489 | return (float) Math.toDegrees(radians);
490 | }
491 |
492 | // 得到两个点的距离
493 | private float spacing(PointF p1, PointF p2) {
494 | float x = p1.x - p2.x;
495 | float y = p1.y - p2.y;
496 | return (float) Math.sqrt(x * x + y * y);
497 | }
498 |
499 | // 得到两个点的中点
500 | private PointF midPoint(PointF p1, PointF p2) {
501 | PointF point = new PointF();
502 | float x = p1.x + p2.x;
503 | float y = p1.y + p2.y;
504 | point.set(x / 2, y / 2);
505 | return point;
506 | }
507 |
508 | // 旋转
509 | private float rotation(PointF p1, PointF p2) {
510 | double delta_x = (p1.x - p2.x);
511 | double delta_y = (p1.y - p2.y);
512 | double radians = Math.atan2(delta_y, delta_x);
513 | return (float) Math.toDegrees(radians);
514 | }
515 |
516 | }
517 |
--------------------------------------------------------------------------------
/AddwordLib/src/main/java/com/funny/addworddemo/view/AddWordOutsideLinearLayout.java:
--------------------------------------------------------------------------------
1 | package com.funny.addworddemo.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Typeface;
9 | import android.os.Build;
10 | import android.util.AttributeSet;
11 | import android.view.Gravity;
12 | import android.view.ViewGroup;
13 | import android.view.ViewTreeObserver;
14 | import android.widget.FrameLayout;
15 | import android.widget.LinearLayout;
16 | import android.widget.TextView;
17 |
18 | import com.funny.addworddemo.AppConst;
19 | import com.funny.addworddemo.R;
20 | import com.funny.addworddemo.util.LogUtils;
21 | import com.funny.addworddemo.util.StringUtils;
22 |
23 | import java.util.ArrayList;
24 | import java.util.List;
25 |
26 | /**
27 | * Created by yue on 2016/4/13.
28 | */
29 | public class AddWordOutsideLinearLayout extends LinearLayout {
30 | private Paint paint;
31 | private int mImageHeight;
32 | private int mImageWidth;
33 | private String text;
34 | private Context context;
35 | private int color = Color.WHITE;
36 | private int size;
37 | private AddWordInsideLinearlayout[] addWordInsideLinearlayouts;
38 | private List ns;
39 | private int orientation;
40 | private int gravity;
41 | private List numberViews;
42 | private float alpha = 1;
43 |
44 | private boolean isSelect = false;
45 | public Bitmap bitDelete = null;
46 | public Bitmap bitMove = null;
47 |
48 | private int bdeleteWidth;
49 | private int ziMarginCount = 0;
50 |
51 | private int maxCount = 0;
52 | private int hangMarginCount = 0;
53 | private int hangCount;
54 | private int layoutWidth;
55 | private boolean isHeng = false;
56 | // Matrix matrix = new Matrix();
57 |
58 | public AddWordOutsideLinearLayout(Context context) {
59 | super(context);
60 | setTextViewOrientation(HORIZONTAL);
61 | this.context = context;
62 | LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
63 | params.gravity = Gravity.LEFT;
64 | this.setLayoutParams(params);
65 | numberViews = new ArrayList();
66 | setWillNotDraw(false);
67 |
68 | paint = new Paint();
69 | paint.setAntiAlias(true);
70 | paint.setColor(Color.parseColor("#F77DA3"));
71 | paint.setStrokeWidth(5f);
72 |
73 | if (bitDelete == null) {
74 | bitDelete = BitmapFactory.decodeResource(getResources(),
75 | R.drawable.btn_sticker_cancel_n);
76 | }
77 | if (bitMove == null) {
78 | bitMove = BitmapFactory.decodeResource(getResources(),
79 | R.drawable.btn_sticker_word_turn_n);
80 | }
81 |
82 | bdeleteWidth = bitDelete.getHeight() / 2;
83 | }
84 |
85 | public AddWordOutsideLinearLayout(Context context, AttributeSet attrs) {
86 | super(context, attrs);
87 | setTextViewOrientation(HORIZONTAL);
88 | this.context = context;
89 | LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
90 | params.gravity = Gravity.LEFT;
91 | this.setLayoutParams(params);
92 | numberViews = new ArrayList();
93 | setWillNotDraw(false);
94 |
95 | paint = new Paint();
96 | paint.setAntiAlias(true);
97 | paint.setColor(Color.parseColor("#F77DA3"));
98 | paint.setStrokeWidth(5f);
99 |
100 | if (bitDelete == null) {
101 | bitDelete = BitmapFactory.decodeResource(getResources(),
102 | R.drawable.btn_sticker_cancel_n);
103 | }
104 | if (bitMove == null) {
105 | bitMove = BitmapFactory.decodeResource(getResources(),
106 | R.drawable.btn_sticker_word_turn_n);
107 | }
108 |
109 | bdeleteWidth = bitDelete.getHeight() / 2;
110 | }
111 |
112 | public int getmImageHeight() {
113 | return mImageHeight;
114 | }
115 |
116 | public void setmImageHeight(int mImageHeight) {
117 | this.mImageHeight = mImageHeight;
118 | }
119 |
120 | public int getmImageWidth() {
121 | return mImageWidth;
122 | }
123 |
124 | public void setmImageWidth(int mImageWidth) {
125 | this.mImageWidth = mImageWidth;
126 | }
127 |
128 | public void setTextViewOrientation(int orientation) {
129 |
130 | if (orientation == LinearLayout.HORIZONTAL) {
131 | if (addWordInsideLinearlayouts != null && addWordInsideLinearlayouts.length > 0) {
132 | removeAllViews();
133 |
134 | for (int b = addWordInsideLinearlayouts.length - 1; b >= 0; b--) {
135 | LayoutParams params1 = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
136 | if (b < addWordInsideLinearlayouts.length - 1) {
137 | params1.leftMargin = hangMarginCount;
138 | }
139 | addWordInsideLinearlayouts[b].setLayoutParams(params1);
140 |
141 | List textViews = addWordInsideLinearlayouts[b].getTextViews();
142 |
143 | for (int a = 0; a < textViews.size(); a++) {
144 | AddWordTextView textView = (AddWordTextView) textViews.get(a);
145 | if (StringUtils.isEnglish(textView.getText().toString())) {
146 | textView.setRotation(90);
147 | }
148 |
149 | LayoutParams params = new LayoutParams(AppConst.textHeight,
150 | ViewGroup.LayoutParams.WRAP_CONTENT);
151 | if (a > 0) {
152 | params.topMargin = ziMarginCount;
153 | // params.leftMargin = hangMarginCount;
154 | }
155 | textView.setLayoutParams(params);
156 |
157 | /*for (int i = 0; i < smallNumbers.length; i++) {
158 | if (smallNumbers[i].equals(textView.getText().toString())) {
159 | textView.setText(bigNumbers[i]);
160 | numberViews.add(textView);
161 | }
162 | }*/
163 | }
164 | }
165 |
166 | for (int i = addWordInsideLinearlayouts.length - 1; i >= 0; i--) {
167 | addView(addWordInsideLinearlayouts[i]);
168 | }
169 |
170 | }
171 | } else {
172 | if (addWordInsideLinearlayouts != null && addWordInsideLinearlayouts.length > 0) {
173 | removeAllViews();
174 |
175 | for (int b = 0; b < addWordInsideLinearlayouts.length; b++) {
176 | LayoutParams params1 = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
177 | if (b > 0) {
178 | params1.topMargin = hangMarginCount;
179 | }
180 | addWordInsideLinearlayouts[b].setLayoutParams(params1);
181 |
182 | List textViews = addWordInsideLinearlayouts[b].getTextViews();
183 |
184 | for (int a = 0; a < textViews.size(); a++) {
185 | AddWordTextView textView = (AddWordTextView) textViews.get(a);
186 | if (StringUtils.isEnglish(textView.getText().toString())) {
187 | textView.setRotation(0);
188 | }
189 |
190 | LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
191 | ViewGroup.LayoutParams.WRAP_CONTENT);
192 | if (a > 0) {
193 | params.leftMargin = ziMarginCount;
194 | // params.topMargin = hangMarginCount;
195 | }
196 | textView.setLayoutParams(params);
197 |
198 | /*for (int i = 0; i < smallNumbers.length; i++) {
199 | if (bigNumbers[i].equals(textView.getText().toString())) {
200 | textView.setText(smallNumbers[i]);
201 | }
202 | }*/
203 | numberViews.clear();
204 | }
205 | }
206 |
207 | for (int i = 0; i < addWordInsideLinearlayouts.length; i++) {
208 | addView(addWordInsideLinearlayouts[i]);
209 | }
210 | }
211 | }
212 |
213 | setOrientation(orientation);
214 | // setGravity(Gravity.CENTER);
215 | this.orientation = orientation;
216 |
217 | if (addWordInsideLinearlayouts != null && addWordInsideLinearlayouts.length > 0) {
218 | setChildViewOrientation(addWordInsideLinearlayouts, orientation);
219 | }
220 |
221 | if (orientation == LinearLayout.HORIZONTAL) {
222 | if (getGravity() == Gravity.LEFT) {
223 | setMyGravity(Gravity.TOP);
224 | } else if (getGravity() == Gravity.RIGHT) {
225 | setMyGravity(Gravity.BOTTOM);
226 | }
227 | } else {
228 | if (getGravity() == Gravity.TOP) {
229 | setMyGravity(Gravity.LEFT);
230 | } else if (getGravity() == Gravity.BOTTOM) {
231 | setMyGravity(Gravity.RIGHT);
232 | }
233 | }
234 | }
235 |
236 | @Override
237 | public int getOrientation() {
238 | return orientation;
239 | }
240 |
241 | public void setChildViewOrientation(AddWordInsideLinearlayout[] addWordInsideLinearlayouts, int orientation) {
242 | if (orientation == LinearLayout.HORIZONTAL) {
243 | for (int i = 0; i < addWordInsideLinearlayouts.length; i++) {
244 | addWordInsideLinearlayouts[i].setTextViewOrientation(LinearLayout.VERTICAL);
245 | }
246 | } else {
247 | for (int i = 0; i < addWordInsideLinearlayouts.length; i++) {
248 | addWordInsideLinearlayouts[i].setTextViewOrientation(LinearLayout.HORIZONTAL);
249 | }
250 | }
251 | }
252 |
253 | public void setMyGravity(int gravity) {
254 | this.gravity = gravity;
255 | setGravity(gravity);
256 | }
257 |
258 | public int getGravity() {
259 | return gravity;
260 | }
261 |
262 | /**
263 | * @param text
264 | */
265 | public void setText(String text) {
266 | this.text = text;
267 | addText();
268 | }
269 |
270 | public String getText() {
271 | return text;
272 | }
273 |
274 | private void addText() {
275 | removeAllViews();
276 | // map.clear();
277 | ns = new ArrayList();
278 | if (text != null && !text.equals("")) {
279 | char[] chars = text.toCharArray();
280 | ns.add(0);
281 | for (int i = 0; i < chars.length; i++) {
282 | if (String.valueOf(text.charAt(i)).equals("\n")) {
283 | ns.add(i);
284 | }
285 | }
286 |
287 | ns.add(text.length());
288 | addWordInsideLinearlayouts = new AddWordInsideLinearlayout[ns.size() - 1];
289 | for (int i = 0; i < ns.size() - 1; i++) {
290 | addWordInsideLinearlayouts[i] = new AddWordInsideLinearlayout(context);
291 | addWordInsideLinearlayouts[i].setTextColor(color);
292 | addWordInsideLinearlayouts[i].setTextSize(size);
293 | String temp = text.substring(ns.get(i), ns.get(i + 1)).trim();
294 | addWordInsideLinearlayouts[i].setText(temp);
295 |
296 | // addView(addWordInsideLinearlayouts[i]);
297 | }
298 |
299 | if (orientation == LinearLayout.HORIZONTAL) {
300 | if (addWordInsideLinearlayouts != null && addWordInsideLinearlayouts.length > 0) {
301 | for (int w = addWordInsideLinearlayouts.length - 1; w >= 0; w--) {
302 | for (TextView textView : addWordInsideLinearlayouts[w].getTextViews()) {
303 | if (StringUtils.isEnglish(textView.getText().toString())) {
304 | textView.setRotation(90);
305 | textView.setLayoutParams(new LayoutParams(AppConst.textHeight,
306 | ViewGroup.LayoutParams.WRAP_CONTENT));
307 | }
308 | }
309 |
310 | /* for (TextView textView : addWordInsideLinearlayouts[w].getTextViews()) {
311 | for (int i = 0; i < smallNumbers.length; i++) {
312 | if (smallNumbers[i].equals(textView.getText().toString())) {
313 | textView.setText(bigNumbers[i]);
314 | numberViews.add(textView);
315 | }
316 | }
317 | }*/
318 |
319 | addView(addWordInsideLinearlayouts[w]);
320 | }
321 | }
322 | } else {
323 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
324 | addView(addWordInsideLinearlayout);
325 | }
326 | }
327 | }
328 |
329 | setChildViewOrientation(addWordInsideLinearlayouts, orientation);
330 |
331 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
332 | this.setLayoutParams(params);
333 | // invalidate();
334 | }
335 |
336 | public void setTextColor(int color) {
337 | this.color = color;
338 | if(addWordInsideLinearlayouts != null && addWordInsideLinearlayouts.length > 0){
339 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
340 | for (int i = 0; i < addWordInsideLinearlayout.getTextViews().size(); i++) {
341 | AddWordTextView text = (AddWordTextView) addWordInsideLinearlayout.getTextViews().get(i);
342 | text.setBorderColor(Color.TRANSPARENT, color, text.isStroke());
343 | }
344 | }
345 | }
346 | }
347 |
348 | public void setTextSize(int size) {
349 | this.size = size;
350 | }
351 |
352 | public void setShadow1() {
353 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
354 | for (TextView textView : addWordInsideLinearlayout.getTextViews()) {
355 | textView.setShadowLayer(0F, 0F, 0F, Color.TRANSPARENT);
356 | }
357 | }
358 | }
359 |
360 | public void setShadow2() {
361 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
362 | for (TextView textView : addWordInsideLinearlayout.getTextViews()) {
363 | textView.setShadowLayer(2F, 2F, 2F, Color.parseColor("#aaaaaa"));
364 | }
365 | }
366 | }
367 |
368 | public void setShadow3() {
369 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
370 | for (TextView textView : addWordInsideLinearlayout.getTextViews()) {
371 | textView.setShadowLayer(4F, 4F, 4F, Color.parseColor("#aaaaaa"));
372 | }
373 | }
374 | }
375 |
376 | public void have_Stroke() {
377 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
378 | for (int i = 0; i < addWordInsideLinearlayout.getTextViews().size(); i++) {
379 | AddWordTextView text = (AddWordTextView) addWordInsideLinearlayout.getTextViews().get(i);
380 | text.setBorderColor(color, Color.TRANSPARENT, true);
381 | }
382 | }
383 | }
384 |
385 | public void no_stroke() {
386 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
387 | for (int i = 0; i < addWordInsideLinearlayout.getTextViews().size(); i++) {
388 | AddWordTextView text = (AddWordTextView) addWordInsideLinearlayout.getTextViews().get(i);
389 | text.setBorderColor(Color.TRANSPARENT, color, false);
390 | }
391 | }
392 |
393 | }
394 |
395 | public void increaseAlpha() {
396 | LogUtils.e("increaseAlpha");
397 | if (alpha < 1) {
398 | alpha = alpha + 0.2f;
399 | }
400 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
401 | for (int i = 0; i < addWordInsideLinearlayout.getTextViews().size(); i++) {
402 | AddWordTextView text = (AddWordTextView) addWordInsideLinearlayout.getTextViews().get(i);
403 | text.setAlpha(alpha);
404 | }
405 | }
406 | }
407 |
408 | public void decreaseAlpha() {
409 | LogUtils.e("decreaseAlpha");
410 | if (alpha > 0) {
411 | alpha = alpha - 0.2f;
412 | }
413 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
414 | for (int i = 0; i < addWordInsideLinearlayout.getTextViews().size(); i++) {
415 | AddWordTextView text = (AddWordTextView) addWordInsideLinearlayout.getTextViews().get(i);
416 | text.setAlpha(alpha);
417 | }
418 | }
419 |
420 | }
421 |
422 | public int getBdeleteWidth() {
423 | return bdeleteWidth;
424 | }
425 |
426 | public void setBdeleteWidth(int bdeleteWidth) {
427 | this.bdeleteWidth = bdeleteWidth;
428 | }
429 |
430 | public boolean isSelect() {
431 | return isSelect;
432 | }
433 |
434 | public void setSelect(boolean isSelect) {
435 | this.isSelect = isSelect;
436 | invalidate();
437 | }
438 |
439 | public void setColor(int color) {
440 | setTextColor(color);
441 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
442 | for (int i = 0; i < addWordInsideLinearlayout.getTextViews().size(); i++) {
443 | AddWordTextView text = (AddWordTextView) addWordInsideLinearlayout.getTextViews().get(i);
444 | text.setTextColor(color);
445 | }
446 | }
447 | }
448 |
449 | public int decreaseZiJianJu() {
450 | if (ziMarginCount >= 0) {
451 | ziMarginCount = ziMarginCount - 2;
452 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
453 | if (maxCount <= addWordInsideLinearlayout.getTextViews().size()) {
454 | maxCount = addWordInsideLinearlayout.getTextViews().size();
455 | }
456 |
457 | for (int i = 0; i < addWordInsideLinearlayout.getTextViews().size(); i++) {
458 | AddWordTextView text = (AddWordTextView) addWordInsideLinearlayout.getTextViews().get(i);
459 | // LinearLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
460 | LayoutParams params = (LayoutParams) text.getLayoutParams();
461 | if (i > 0) {
462 | if (orientation == LinearLayout.HORIZONTAL) {
463 | params.topMargin = ziMarginCount;
464 | } else {
465 | params.leftMargin = ziMarginCount;
466 | }
467 | }
468 | text.setLayoutParams(params);
469 | }
470 |
471 | // addWordInsideLinearlayout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
472 | this.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT));
473 |
474 | }
475 | }
476 | return maxCount;
477 | }
478 |
479 | public int increaseZiJianJu() {
480 | ziMarginCount = ziMarginCount + 2;
481 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
482 | if (maxCount <= addWordInsideLinearlayout.getTextViews().size()) {
483 | maxCount = addWordInsideLinearlayout.getTextViews().size();
484 | }
485 |
486 | for (int i = 0; i < addWordInsideLinearlayout.getTextViews().size(); i++) {
487 | AddWordTextView text = (AddWordTextView) addWordInsideLinearlayout.getTextViews().get(i);
488 | LayoutParams params = (LayoutParams) text.getLayoutParams();
489 | // LinearLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
490 |
491 | if (i > 0) {
492 | if (orientation == LinearLayout.HORIZONTAL) {
493 | params.topMargin = ziMarginCount;
494 | } else {
495 | params.leftMargin = ziMarginCount;
496 | }
497 | }
498 | text.setLayoutParams(params);
499 | }
500 |
501 | // addWordInsideLinearlayout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
502 | this.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT));
503 |
504 | }
505 | return maxCount;
506 | }
507 |
508 | public int increaseHangJianJu() {
509 | hangMarginCount = hangMarginCount + 2;
510 | for (int i = 0; i < addWordInsideLinearlayouts.length; i++) {
511 | LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
512 | if (i > 0) {
513 | if (orientation == LinearLayout.HORIZONTAL) {
514 | params.rightMargin = hangMarginCount;
515 | } else {
516 | params.topMargin = hangMarginCount;
517 | }
518 | }
519 | addWordInsideLinearlayouts[i].setLayoutParams(params);
520 | }
521 |
522 | hangCount = addWordInsideLinearlayouts.length;
523 | return hangCount;
524 | }
525 |
526 | public int decreaseHangJianJu() {
527 | if (hangMarginCount >= 0) {
528 | hangMarginCount = hangMarginCount - 2;
529 | for (int i = 0; i < addWordInsideLinearlayouts.length; i++) {
530 | LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
531 | if (i > 0) {
532 | if (orientation == LinearLayout.HORIZONTAL) {
533 | params.rightMargin = hangMarginCount;
534 | } else {
535 | params.topMargin = hangMarginCount;
536 | }
537 | }
538 |
539 | addWordInsideLinearlayouts[i].setLayoutParams(params);
540 | }
541 | }
542 | hangCount = addWordInsideLinearlayouts.length;
543 | return hangCount;
544 | }
545 |
546 | public interface OnLayoutWidth {
547 | void layout(int width, int height);
548 | }
549 |
550 | public void layoutWidthAndHeight(final AddWordOutsideLinearLayout addWordOutsideLinearLayout, final OnLayoutWidth onLayoutWidth) {
551 | addWordOutsideLinearLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
552 | @Override
553 | public void onGlobalLayout() {
554 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
555 | addWordOutsideLinearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
556 | }
557 | onLayoutWidth.layout(addWordOutsideLinearLayout.getMeasuredWidth(), addWordOutsideLinearLayout.getMeasuredHeight());
558 | }
559 | });
560 | }
561 |
562 | public int layoutHeight() {
563 | int maxTextsize = 0;
564 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
565 | if (maxTextsize <= addWordInsideLinearlayout.getTextViews().size()) {
566 | maxTextsize = addWordInsideLinearlayout.getTextViews().size();
567 | }
568 | }
569 |
570 |
571 | return AppConst.textHeight * maxTextsize +
572 | (maxTextsize - 1) * ziMarginCount;
573 | }
574 |
575 | public void setTypeFace(Typeface face) {
576 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
577 | for (TextView view : addWordInsideLinearlayout.getTextViews()) {
578 | view.setTypeface(face);
579 | }
580 | }
581 | }
582 |
583 | public String getAllText(){
584 | String str = "";
585 | for (AddWordInsideLinearlayout addWordInsideLinearlayout : addWordInsideLinearlayouts) {
586 | for(TextView textView:addWordInsideLinearlayout.getTextViews()){
587 | str += textView.getText();
588 | }
589 | str += "\n";
590 | }
591 | return str;
592 | }
593 | }
594 |
--------------------------------------------------------------------------------