[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n"
  },
  {
    "path": ".idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"distributionType\" value=\"DEFAULT_WRAPPED\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/app\" />\n          </set>\n        </option>\n        <option name=\"resolveModulePerSourceSet\" value=\"false\" />\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"NullableNotNullManager\">\n    <option name=\"myDefaultNullable\" value=\"android.support.annotation.Nullable\" />\n    <option name=\"myDefaultNotNull\" value=\"android.support.annotation.NonNull\" />\n    <option name=\"myNullables\">\n      <value>\n        <list size=\"5\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.Nullable\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nullable\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"javax.annotation.CheckForNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.Nullable\" />\n          <item index=\"4\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.Nullable\" />\n        </list>\n      </value>\n    </option>\n    <option name=\"myNotNulls\">\n      <value>\n        <list size=\"4\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.NotNull\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nonnull\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.NonNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.NonNull\" />\n        </list>\n      </value>\n    </option>\n  </component>\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_1_7\" project-jdk-name=\"1.8\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/build/classes\" />\n  </component>\n  <component name=\"ProjectType\">\n    <option name=\"id\" value=\"Android\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n      <module fileurl=\"file://$PROJECT_DIR$/TagImageView.iml\" filepath=\"$PROJECT_DIR$/TagImageView.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/app/app.iml\" filepath=\"$PROJECT_DIR$/app/app.iml\" />\n    </modules>\n  </component>\n</project>"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <option name=\"ignoredProducers\">\n      <set>\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer\" />\n      </set>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": "README.md",
    "content": "# 高仿小红书之标签添加功能 \n   <ol>\n\t<li>随点击处添加标签</li>\n\t<li>计算标签位置</li>\n\t<li>可将标签位置还原渲染至不同屏幕尺寸</li>\n\t<li>拖拽删除标签</li>\n\t<li>可拖拽时支持点击标签更换文字方向</li>\n\t<li>不可拖拽时支持点击标签响应点击事件</li>\n   </ol>\n\n\n未做的：\n当标签贴边，文字框将会收缩。\n\n\n## 效果图\n\n![](video.gif)\n\n\n### Log\n\n![](log.jpg)\n\n## 圆点相关数据\n\n 圆点坐标 x == 348  , y == 825\n\n 圆点在图片上的坐标百分比%  x  == 0.32222223  , y == 0.5729167\n\n 圆点数据：\n\n\tTagInfoBean{\n\t\tname='￥55 粉色衣服',\n\t\tnotesTagType=3, \n\t\turl='tag点的链接url', \n\t\tx=0.3222222328186035, \n\t\ty=0.5729166865348816, \n\t\twidth=1080.0, \n\t\theight=1440.0, \n\t\tpicWidth=1010.0, \n\t\tpicHeight=1324.0, \n\t\tnotesTagId=652, \n\t\tisLeft=true, \n\t\tisCanMove=true, \n\t\tindex=1\n\t}\n\n\n\n## Bean\n\n    private String name;                  //标签内容\n\n    private int notesTagType;             //标签type\n  \n    private String url;                   //标签url\n\n    private double x;                     //圆心x的在父控件位置 %\n\n    private double y;                     //圆心y的在父控件位置 %\n\n    private float width;                  //控件宽度\n\n    private float height;                 //控件高度\n\n    private float picWidth;               //图片的宽度\n\n    private float picHeight;              //图片的高度\n\n    private int notesTagId;               //标签id\n\n    private boolean isLeft = true;        //圆点是否在左边\n\n    private boolean isCanMove = true;     //标签是否可以移动\n\n    private int index;                    //用来记录在编辑标签中的index 位置\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 26\n    defaultConfig {\n        applicationId \"com.ken.tagimage\"\n        minSdkVersion 16\n        targetSdkVersion 26\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation 'com.android.support:appcompat-v7:27+'\n    implementation 'com.android.support.constraint:constraint-layout:1.1.2'\n    testImplementation 'junit:junit:4.12'\n    androidTestImplementation 'com.android.support.test:runner:1.0.2'\n    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "app/src/androidTest/java/com/ken/tagimage/ExampleInstrumentedTest.java",
    "content": "package com.ken.tagimage;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.ken.tagimage\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.ken.tagimage\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\"\n            >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/ken/tagimage/Density.java",
    "content": "/*\n * Copyright (c) 2014,KJFrameForAndroid Open Source Project,张涛.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.ken.tagimage;\n\nimport android.content.Context;\nimport android.util.DisplayMetrics;\n\n/**\n * 作者: by KEN on 2018/7/14 17.\n * 邮箱: gr201655@163.com\n */\n\n\npublic final class Density {\n\n    /**\n     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)\n     */\n    public static int dip2px(Context context, float dpValue) {\n        final float scale = context.getResources().getDisplayMetrics().density;\n        return (int) (dpValue * scale + 0.5f);\n    }\n\n\n    public static float dip2pxForFloat(Context context, float dpValue) {\n        final float scale = context.getResources().getDisplayMetrics().density;\n        return (dpValue * scale + 0.5f);\n    }\n\n\n    /**\n     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp\n     */\n    public static int px2dip(Context context, float pxValue) {\n        final float scale = context.getResources().getDisplayMetrics().density;\n        return (int) (pxValue / scale + 0.5f);\n    }\n\n    /**\n     * 根据手机的分辨率从 px(像素) 的单位 转成为 sp\n     */\n    public static int px2sp(Context context, float pxValue) {\n        float fontScale = context.getResources().getDisplayMetrics().scaledDensity;\n        return (int) (pxValue / fontScale + 0.5f);\n    }\n\n    /**\n     * 根据手机的分辨率从 sp 的单位 转成为 px\n     */\n    public static int sp2px(Context context, float spValue) {\n        float fontScale = context.getResources().getDisplayMetrics().scaledDensity;\n        return (int) (spValue * fontScale + 0.5f);\n    }\n\n    /**\n     * 获取dialog宽度\n     */\n    public static int getDialogW(Context aty) {\n        DisplayMetrics dm = new DisplayMetrics();\n        dm = aty.getResources().getDisplayMetrics();\n        int w = dm.widthPixels - 100;\n        // int w = aty.getWindowManager().getDefaultDisplay().getWidth() - 100;\n        return w;\n    }\n\n    /**\n     * 获取屏幕宽度\n     */\n    public static int getScreenW(Context aty) {\n        DisplayMetrics dm = new DisplayMetrics();\n        dm = aty.getResources().getDisplayMetrics();\n        int w = dm.widthPixels;\n        // int w = aty.getWindowManager().getDefaultDisplay().getWidth();\n        return w;\n    }\n\n\n    /**\n     * 获取屏幕高度\n     */\n    public static int getScreenH(Context aty) {\n        DisplayMetrics dm = new DisplayMetrics();\n        dm = aty.getResources().getDisplayMetrics();\n        int h = dm.heightPixels;\n        // int h = aty.getWindowManager().getDefaultDisplay().getHeight();\n        return h;\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/ken/tagimage/MainActivity.java",
    "content": "package com.ken.tagimage;\n\nimport android.content.pm.ActivityInfo;\nimport android.os.Bundle;\nimport android.support.annotation.NonNull;\nimport android.support.v7.app.AppCompatActivity;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.widget.FrameLayout;\nimport android.widget.Toast;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MainActivity extends AppCompatActivity {\n\n    private int imageH;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);\n        requestWindowFeature(Window.FEATURE_NO_TITLE);\n        //禁止横屏\n        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);\n        setContentView(R.layout.activity_main);\n        final TagImageView tag_view = findViewById(R.id.tag_content);\n        tag_view.setClickTagListener(new TagImageView.ClickTagListener() {\n            @Override\n            public void click(TagInfoBean bean) {\n                Toast.makeText(MainActivity.this, \"标签被点击 == \" + bean.getName(), Toast.LENGTH_SHORT).show();\n            }\n        });\n        //添加标签\n        tag_view.setAddTagListener(new TagImageView.AddTagListener() {\n            @Override\n            public void addTag(String path, double rawX, double rawY) {\n\n\n                final TagInfoBean bean = new TagInfoBean();\n                bean.setCanMove(true);\n                bean.setNotesTagId(652);\n                bean.setNotesTagType(TagTextView.TAG_TEXT);\n                //通过手机中的图片地址  或者  网络拉取的图片信息  获得图片宽高\n                bean.setPicWidth(1010);\n                bean.setPicHeight(1324);\n                bean.setUrl(\"tag点的链接url\");\n                // 显示控件的显示 依照图片的本身的宽高比例进行动态设置\n                bean.setWidth(Density.getScreenW(MainActivity.this));\n                //标签在控件上的比例\n                bean.setLeft(!(rawX > bean.getWidth()/ 2));\n                bean.setX(rawX / bean.getWidth());\n                bean.setY(rawY / imageH);\n                bean.setHeight(imageH);\n                ViewDialogFragment dialogFragment = new ViewDialogFragment();\n                dialogFragment.setCallback(new ViewDialogFragment.Callback() {\n                    @Override\n                    public void onClick(String tabName) {\n                        if (TextUtils.isEmpty(tabName))\n                            tabName = \"女孩\";\n                        bean.setName(tabName);\n                        Log.e(\"zz\", \"onClick: \"+bean.getName() + \"  \" + imageH );\n\n\n                        tag_view.addTag(bean);\n                    }\n                });\n                dialogFragment.show(getSupportFragmentManager());\n            }\n        });\n        //删除标签\n        tag_view.setDeleteTagListener(new TagImageView.DeleteTagListener() {\n            @Override\n            public void remove(String path, TagInfoBean bean) {\n                Toast.makeText(MainActivity.this, \"删除标签 == \" + bean.getName(), Toast.LENGTH_SHORT).show();\n            }\n        });\n        //设置图片的路径\n        tag_view.setPath(\"一般是本地图片地址,这里用的是资源图片\");  //可用来标记这些标签属于哪张图片\n\n        //添加初始标签\n        List<TagInfoBean> tagInfoBeanList = new ArrayList<>();\n\n        tagInfoBeanList.add(createTag1());\n        tagInfoBeanList.add(createTag2());\n        tagInfoBeanList.add(createTag3());\n\n        //设置 图片的高度  可根据实际的图片高度比例  设置\n        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, imageH);\n        findViewById(R.id.image).setLayoutParams(params);\n\n        tag_view.setLayoutParams(params);\n        tag_view.setTagList(tagInfoBeanList);\n    }\n\n    @NonNull\n    private TagInfoBean createTag1() {\n        TagInfoBean bean = new TagInfoBean();\n        //该标签是否可以移动\n        bean.setCanMove(false);\n        bean.setLeft(false);\n        bean.setName(\"一杯奶茶\");\n        bean.setNotesTagId(652);\n        bean.setNotesTagType(TagTextView.TAG_BRAND);\n\n        //通过手机中的图片地址  或者  网络拉取的图片信息  获得图片宽高\n        bean.setPicWidth(1010);\n        bean.setPicHeight(1324);\n        bean.setUrl(\"tag点的链接url\");\n        // 显示控件的显示 依照图片的本身的宽高比例进行动态设置\n        bean.setWidth(Density.getScreenW(this));\n        imageH = 0;\n        //项目中需求是只有1：1  和  3：4的比例   这个可根据实际修改   直接按图片比例也可以\n        if (bean.getPicWidth() / bean.getPicHeight() > 0.85f) {\n            imageH = (int) bean.getWidth();\n        } else {\n            imageH = (int) (bean.getWidth() * 4 / 3);\n        }\n\n        //标签原点在照片上的比例\n        bean.setX(0.7513889);\n        bean.setY(0.5864583);\n        bean.setHeight(imageH);\n        return bean;\n    }\n\n    @NonNull\n    private TagInfoBean createTag2() {\n        TagInfoBean bean = new TagInfoBean();\n        bean.setCanMove(true);\n        bean.setLeft(true);\n        bean.setName(\"￥55 粉色衣服\");\n        bean.setNotesTagId(652);\n        bean.setNotesTagType(TagTextView.TAG_PRICE);\n\n        //通过手机中的图片地址  或者  网络拉取的图片信息  获得图片宽高\n        bean.setPicWidth(1010);\n        bean.setPicHeight(1324);\n        bean.setUrl(\"tag点的链接url\");\n        // 显示控件的显示 依照图片的本身的宽高比例进行动态设置\n        bean.setWidth(Density.getScreenW(this));\n        imageH = 0;\n        //项目中需求是只有1：1  和  3：4的比例   这个可根据实际修改   直接按图片比例也可以\n        if (bean.getPicWidth() / bean.getPicHeight() > 0.85f) {\n            imageH = (int) bean.getWidth();\n        } else {\n            imageH = (int) (bean.getWidth() * 4 / 3);\n        }\n\n        //标签原点在照片上的比例\n        bean.setX(0.5625);\n        bean.setY(0.81041664);\n        bean.setHeight(imageH);\n        return bean;\n    }\n    @NonNull\n    private TagInfoBean createTag3() {\n        TagInfoBean bean = new TagInfoBean();\n        bean.setCanMove(true);\n        bean.setLeft(true);\n        bean.setName(\"大眼睛\");\n        bean.setNotesTagId(652);\n        bean.setNotesTagType(TagTextView.TAG_BRAND);\n\n        //通过手机中的图片地址  或者  网络拉取的图片信息  获得图片宽高\n        bean.setPicWidth(1010);\n        bean.setPicHeight(1324);\n        bean.setUrl(\"tag点的链接url\");\n        // 显示控件的显示 依照图片的本身的宽高比例进行动态设置\n        bean.setWidth(Density.getScreenW(this));\n        imageH = 0;\n        //计算图片的高度   因为项目中需求是图片只有1：1  和  3：4的比例   这个可根据实际修改   直接按图片比例也可以\n        if (bean.getPicWidth() / bean.getPicHeight() > 0.85f) {\n            imageH = (int) bean.getWidth();\n        } else {\n            imageH = (int) (bean.getWidth() * 4 / 3);\n        }\n\n        //标签原点在照片上的比例\n        bean.setX(0.35833332);\n        bean.setY(0.29583332);\n        bean.setHeight(imageH);\n        return bean;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/ken/tagimage/TagImageView.java",
    "content": "package com.ken.tagimage;\n\nimport android.content.Context;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.animation.CycleInterpolator;\nimport android.widget.FrameLayout;\nimport android.widget.RelativeLayout;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 作者: by KEN on 2018/7/14 17.\n * 邮箱: gr201655@163.com\n */\n\npublic class TagImageView extends FrameLayout {\n\n    private RelativeLayout mContentLayout;\n    private Paint mOvalPaint;\n    private float topPadding;\n    private int leftPading;\n    private int mRadius;\n    private int lineLong;\n    private View mDelete_tags;\n    private TagTextView.TagGestureListener tagClickListener;\n    private boolean isClick = false;\n    private String mPath;\n    /**\n     * 标签数据   最后只要重新设置每个标签当前的位置\n     * 标签有可能被编辑 所以要获取最后的位置 即可\n     * 如果标签是不可移动的属性 则不需要更新\n     */\n    private List<TagInfoBean> infoBeanList = new ArrayList<>();\n    public TagImageView(Context context) {\n        this(context, null);\n    }\n\n    public TagImageView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public TagImageView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        initView(context);\n    }\n\n    private void initView(Context context) {\n        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n        inflater.inflate(R.layout.image_tag, this, true);\n        mDelete_tags = findViewById(R.id.delete_tags);\n        mDelete_tags.setVisibility(View.GONE);\n\n        mContentLayout = (RelativeLayout) findViewById(R.id.tagsGroup);\n        mOvalPaint = new Paint();\n        mOvalPaint.setTextSize(Density.sp2px(context, 14));\n        mOvalPaint.setStrokeWidth(Density.dip2px(context, 1));\n        mOvalPaint.setFilterBitmap(true);\n\n\n\n        //滑动\n        mContentLayout.setOnTouchListener(onTouchListener);\n\n        //边框与文字顶部底部的距离\n        topPadding = Density.dip2px(context, 3);\n        leftPading = Density.dip2px(context, 6);\n        mRadius = Density.dip2px(context, 4);\n        //线长\n        lineLong = Density.dip2px(context, 15);\n\n        //标签手势监听\n        tagClickListener = new TagTextView.TagGestureListener() {\n            @Override\n            public void onDown(View view, TagInfoBean bean) {\n                if (mDelete_tags.getVisibility() == View.GONE && bean.isCanMove()) {\n                    mDelete_tags.setVisibility(View.VISIBLE);\n                }\n            }\n\n            @Override\n            public void onUp(View view, TagInfoBean bean) {\n                if (mDelete_tags.getVisibility() == View.VISIBLE) {\n                    mDelete_tags.setVisibility(View.GONE);\n                }\n            }\n\n            @Override\n            public void clickTag(View view, TagInfoBean bean) {\n                if(mClickTagListener!=null)\n                    mClickTagListener.click(bean);\n            }\n\n            @Override\n            public void inDeleteRect(View view, TagInfoBean bean) {\n                mContentLayout.removeView(view);\n                infoBeanList.remove(bean);\n                //移除的监听\n                if (mDeleteTagListener != null) mDeleteTagListener.remove(mPath, bean);\n\n            }\n\n            @Override\n            public void move(View view, TagInfoBean bean) {\n\n            }\n        };\n    }\n\n\n    float mLastX;\n    float downX;\n    float downY;\n    OnTouchListener onTouchListener = new OnTouchListener() {\n        @Override\n        public boolean onTouch(View v, MotionEvent event) {\n\n            switch (event.getAction()) {\n                case MotionEvent.ACTION_DOWN:\n                     downX = event.getRawX();\n                     downY = event.getRawY();\n\n                    mLastX = event.getX();\n                    isClick = true;\n                    break;\n                case MotionEvent.ACTION_UP:\n                    if (isClick) {\n                        double rawX = event.getRawX();\n                        double rawY = event.getRawY();\n\n                        mAddTagListener.addTag(mPath, downX, downY);\n                    }\n\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    if (isClick && Math.abs(event.getX() - mLastX) > 50f) {\n                        isClick = false;\n                    }\n                    return false;\n            }\n\n            return true;\n        }\n    };\n\n\n    public void addTag(TagInfoBean infoBean) {\n        infoBeanList.add(infoBean);\n        createTags(infoBean);\n    }\n\n    public void setAddTagListener(AddTagListener addTagListener) {\n        mAddTagListener = addTagListener;\n    }\n\n    public void setDeleteTagListener(DeleteTagListener deleteTagListener) {\n        mDeleteTagListener = deleteTagListener;\n    }\n\n    /**\n     * 添加标签\n     */\n    private AddTagListener mAddTagListener;\n    public interface AddTagListener {\n        void addTag(String path, double rawX, double rawY);\n    }\n\n    /**\n     * 删除标签\n     */\n    private DeleteTagListener mDeleteTagListener;\n\n    public interface DeleteTagListener {\n        void remove(String path, TagInfoBean bean);\n    }\n\n    /**\n     * 标签被点击\n     */\n    private ClickTagListener mClickTagListener;\n\n    public void setClickTagListener(ClickTagListener mClickTagListener) {\n        this.mClickTagListener = mClickTagListener;\n    }\n\n    public interface ClickTagListener {\n        void click(TagInfoBean bean);\n    }\n\n    /**\n     * 图片在本地的地址\n     *\n     * @param path\n     */\n    public void setPath(String path) {\n        mPath = path;\n    }\n\n    public String getPath() {\n        return mPath;\n    }\n\n\n\n    public void setTagList(List<TagInfoBean> list) {\n        clearTags();\n        int num = 0;//用于计数\n        infoBeanList = list;\n        for (TagInfoBean bean : list) {\n            if (TextUtils.isEmpty(bean.getName())) {\n                continue;\n            }\n            bean.setIndex(num++);\n            createTags(bean);\n        }\n    }\n\n\n\n    private void createTags(TagInfoBean bean) {\n        //获取文本的宽高\n        final Rect bounds = new Rect();\n        String name = bean.getName();\n        if (name.length() > 16) {\n            name = name.substring(0, 16) + \"...\";\n        }\n\n        mOvalPaint.getTextBounds(name, 0, name.length(), bounds);\n        //获得边框的宽高\n        final float mStokeHeight = bounds.bottom - bounds.top + topPadding * 2;\n\n        int left = 0;\n        int top = 0;\n        top = (int) (bean.getHeight() * bean.getY() - mStokeHeight / 2);\n        if (bean.isLeft()) {\n            left = (int) (bean.getWidth() * bean.getX() - mRadius);\n        } else {\n            int mStokeWidth = bounds.right - bounds.left + leftPading * 2 + (bounds.bottom - bounds.top);\n            left = (int) (bean.getWidth() * bean.getX() - mStokeWidth - lineLong);\n        }\n        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout\n                .LayoutParams\n                .WRAP_CONTENT);\n\n        params.setMargins(left, top, 0, 0);\n        final TagTextView child = new TagTextView(getContext(), bean);\n\n        child.setTagGestureListener(tagClickListener);\n        mContentLayout.addView(child, params);\n    }\n\n\n    private void clearTags() {\n        mContentLayout.removeAllViews();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/ken/tagimage/TagInfoBean.java",
    "content": "package com.ken.tagimage;\n\n/**\n * 作者: by KEN on 2018/7/14 17.\n * 邮箱: gr201655@163.com\n *\n * 由于项目为统一跟后台定义字段相同  有部分命名不准确\n */\n\n\npublic class TagInfoBean {\n    private String name;         //标签内容\n    private int notesTagType;    //标签type\n    private String url;          //标签url\n\n    private double x;  //圆心x的在父控件位置 %\n    private double y;  //圆心y的在父控件位置 %\n\n    private float width;   //控件宽度\n    private float height;  //控件高度\n\n    private float picWidth;  //图片的宽度\n    private float picHeight; //图片的高度\n\n    private int notesTagId;  //标签id\n\n    private boolean isLeft = true;  //圆点是否在左边\n\n    private boolean isCanMove = true;  //标签是否可以移动\n\n    private int index;    //用来记录在编辑标签中的index 位置\n\n    public void setCanMove(boolean canMove) {\n        isCanMove = canMove;\n    }\n\n    public boolean isCanMove() {\n        return isCanMove;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n\n    public int getNotesTagType() {\n        return notesTagType;\n    }\n\n    public void setNotesTagType(int notesTagType) {\n        this.notesTagType = notesTagType;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    public double getX() {\n        return x;\n    }\n\n    public void setX(double x) {\n        this.x = x;\n    }\n\n    public float getPicWidth() {\n        return picWidth;\n    }\n\n    public void setPicWidth(float picWidth) {\n        this.picWidth = picWidth;\n    }\n\n    public float getPicHeight() {\n        return picHeight;\n    }\n\n    public void setPicHeight(float picHeight) {\n        this.picHeight = picHeight;\n    }\n\n    public double getY() {\n        return y;\n    }\n\n    public void setY(double y) {\n        this.y = y;\n    }\n\n    public float getWidth() {\n        return width;\n    }\n\n    public void setWidth(float width) {\n        this.width = width;\n    }\n\n    public float getHeight() {\n        return height;\n    }\n\n    public void setHeight(float height) {\n        this.height = height;\n    }\n\n    public int getNotesTagId() {\n        return notesTagId;\n    }\n\n    public void setNotesTagId(int notesTagId) {\n        this.notesTagId = notesTagId;\n    }\n\n    public boolean isLeft() {\n        return isLeft;\n    }\n\n    public void setLeft(boolean left) {\n        isLeft = left;\n    }\n\n    public void setIndex(int index) {\n        this.index = index;\n    }\n\n    public int getIndex() {\n        return index;\n    }\n\n    @Override\n    public String toString() {\n        return \"TagInfoBean{\" +\n                \"name='\" + name + '\\'' +\n                \", notesTagType=\" + notesTagType +\n                \", url='\" + url + '\\'' +\n                \", x=\" + x +\n                \", y=\" + y +\n                \", width=\" + width +\n                \", height=\" + height +\n                \", picWidth=\" + picWidth +\n                \", picHeight=\" + picHeight +\n                \", notesTagId=\" + notesTagId +\n                \", isLeft=\" + isLeft +\n                \", isCanMove=\" + isCanMove +\n                \", index=\" + index +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/ken/tagimage/TagTextView.java",
    "content": "package com.ken.tagimage;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.PaintFlagsDrawFilter;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.support.v4.view.GestureDetectorCompat;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.View;\n\n\n/**\n * 作者: by KEN on 2018/7/14 17.\n * 邮箱: gr201655@163.com\n */\n\npublic class TagTextView extends View {\n\n\n    private Paint ovalPaint = new Paint();\n\n    private float mRadius = 0;   //外圆半径\n    private float mInnerRadius = 0;       //内圆半径\n    private RectF mCenterRect;\n    private int lineLong = 0; //横线的长度\n    private RectF mTextRoundRect;   //文字框内容高度\n    private String textContent;    //文本内容\n    private final int TextMaxNum = 16;  //文本最多显示16个文字\n    private GestureDetectorCompat mGestureDetector;\n\n    private float mStokeHeight;  //边框的高度\n    private float mStokeWidth;   //边框的宽度\n    private float mCircleY;   //中心圆的高度\n    private float leftPadding;\n    private float mTextX;     //文字起始x\n    private float mTextY;    //文字baseline的高度\n    private int mRoundX;\n\n    private float mFirstDownX;\n    private float mFirstDownY;\n\n    private int leftBorder = 0;     //上边界  左边界\n    private int rightBorder = 0;   //右边界\n    private int bottomBorder = 0;  //底部边界\n\n    private int circleX = 0;     //中心圆的x坐标\n    private int circleY = 0;     //中心圆的y坐标\n\n    private int parentWidth = 0;  //父级的宽度\n    private int parentHeight = 0;  //父级的高度\n\n    private float percentX = 0f;   //在父级的宽度占比\n    private float percentY = 0f;   //在父级的高度占比\n\n    private boolean isLeft = true;  //圆点是否在左边\n    private TagInfoBean mTagInfoBean;\n    private int mShadeColor;\n\n    public static final int TAG_TEXT = 0;  //一般\n    public static final int TAG_LOCATION = 1;  //地址\n    public static final int TAG_BRAND = 2;  //品牌\n    public static final int TAG_PRICE = 3; //价格\n\n    private boolean mCanMove = true; //可以被移动\n\n    private float tempX;   // 临时记录按键按下的位置\n    private float tempY;\n\n    private Bitmap mBitmap;\n    private float mTopPadding;\n    private RectF mTypeIconRect;\n    private int mIconWidth;\n\n    private boolean isMoveXY = false;  // 用来记录是否被移动过  如果没有则不改变数据中的 xy百分比\n\n    private RectF deleteRect;     //用来判断是否滑动到底部\n\n    private PaintFlagsDrawFilter pfd;\n\n    public TagInfoBean getTagInfoBean() {\n        if (isMoveXY) {\n            mTagInfoBean.setX(percentX);\n            mTagInfoBean.setY(percentY);\n        }\n        return mTagInfoBean;\n    }\n\n    public TagTextView(Context context, TagInfoBean tagInfoBean) {\n        this(context, null, 0);\n        mTagInfoBean = tagInfoBean;\n        initView(context);\n    }\n\n    public TagTextView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public TagTextView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n\n        initView(context);\n    }\n\n    private void initView(Context context) {\n        if (mTagInfoBean == null) return;\n        //在1+手机滑动时出现了残影   也有可能是硬件加速的问题\n       // setFadingEdgeLength(0);\n      //  setLayerType(View.LAYER_TYPE_SOFTWARE, null);\n\n        ovalPaint.setAntiAlias(true);\n        ovalPaint.setStyle(Paint.Style.FILL);\n        ovalPaint.setColor(Color.WHITE);\n        ovalPaint.setStrokeWidth(Density.dip2px(context, 1));\n        mTextRoundRect = new RectF();\n        mShadeColor = Color.parseColor(\"#30ffffff\");\n        mRadius = Density.dip2px(context, 4);\n        mInnerRadius = Density.dip2px(context, 2.5f);\n\n        BitmapFactory.Options opts = new BitmapFactory.Options();\n        opts.inPreferredConfig = Bitmap.Config.ARGB_8888;\n\n        switch (mTagInfoBean.getNotesTagType()) {\n            case TAG_LOCATION:\n                mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.tags_location, opts);\n                break;\n            case TAG_BRAND:\n                mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.brand, opts);\n                break;\n            case TAG_PRICE:\n                mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.price, opts);\n                break;\n            default:\n                mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.tags_text, opts);\n                break;\n        }\n        //防止bitmap 变模糊\n        ovalPaint.setFilterBitmap(true);\n        ovalPaint.setAntiAlias(true);\n        ovalPaint.setStrokeWidth(Density.dip2px(getContext(),1));\n        pfd = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);\n        ovalPaint.setTextSize(Density.dip2px(context, 14));\n        lineLong = Density.dip2px(context, 15);\n        textContent = mTagInfoBean.getName();\n        if (TextUtils.isEmpty(textContent)) return;\n        if (textContent.length() > TextMaxNum) {\n            textContent = textContent.substring(0, TextMaxNum - 1) + \"...\";\n        }\n        mCanMove = mTagInfoBean.isCanMove();\n        rightBorder = Density.getScreenW(context);\n\n\n        //获取文本的宽高\n        Rect bounds = new Rect();\n        ovalPaint.getTextBounds(textContent, 0, textContent.length(), bounds);\n\n        //边框与文字顶部底部的距离\n        mTopPadding = Density.dip2px(context, 3);\n        //边框与文字左右的距离\n        leftPadding = Density.dip2px(context, 6);\n\n        isLeft = mTagInfoBean.isLeft();\n\n        //标签类别icon\n        mTypeIconRect = new RectF();\n        int iconHeight = bounds.bottom - bounds.top;\n        mIconWidth = iconHeight;\n\n        mTypeIconRect.set(lineLong + mRadius + leftPadding, mTopPadding, lineLong + mRadius + mIconWidth + leftPadding, mTopPadding + iconHeight);\n\n        //获得边框的宽高\n        mStokeHeight = bounds.bottom - bounds.top + mTopPadding * 2;\n        mStokeWidth = bounds.right - bounds.left + leftPadding * 2 + mIconWidth;\n\n        //文字Rect的大小\n        mTextRoundRect.set(lineLong + mRadius, 0, lineLong + mRadius + mStokeWidth,  mStokeHeight);\n\n\n        //圆心y\n        mCircleY = mStokeHeight / 2;\n\n        //文字起始x\n        mTextX = lineLong + mRadius + leftPadding + mIconWidth;\n\n        //文字y\n        Paint.FontMetricsInt fontMetrics = ovalPaint.getFontMetricsInt();\n        mTextY = (mTextRoundRect.top + mTextRoundRect.bottom - fontMetrics.bottom -\n                fontMetrics.top) / 2;\n\n        //整个的控件的位置\n        mCenterRect = new RectF();\n        mCenterRect.set(0, 0, mRadius + lineLong + mStokeWidth, mStokeHeight);\n\n        //手势监听\n        mGestureDetector = new GestureDetectorCompat(context, mGestureListener);\n        mGestureDetector.setIsLongpressEnabled(false);\n\n        parentWidth = rightBorder;\n        parentHeight = (int) mTagInfoBean.getHeight();\n\n        bottomBorder = parentHeight;\n\n        //删除--控件的半径\n        int delet_raduis = Density.dip2px(context, 15);\n        float top = parentHeight - Density.dip2px(context, 80) - mStokeHeight / 2;\n\n        //删除图标的大小\n        deleteRect = new RectF(rightBorder / 2 - (lineLong + mStokeWidth + delet_raduis), top,\n                rightBorder / 2 + delet_raduis, parentHeight - Density.dip2px(context, 50) - mStokeHeight / 2);\n\n    }\n\n\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (mRadius + lineLong + mStokeWidth +ovalPaint.getStrokeWidth() * 2), MeasureSpec.EXACTLY);\n        heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (mStokeHeight + ovalPaint.getStrokeWidth() * 2), MeasureSpec.EXACTLY);\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        //边框圆角半径\n        mRoundX = h / 2;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        //绘制外圆\n        ovalPaint.setStyle(Paint.Style.FILL);\n        ovalPaint.setColor(mShadeColor);\n\n        //原点在左边\n        if (isLeft) {\n            canvas.drawCircle(mRadius, mCircleY + ovalPaint.getStrokeWidth(), mRadius, ovalPaint);\n            mTextRoundRect.set(lineLong + mRadius, ovalPaint.getStrokeWidth(), lineLong + mRadius + mStokeWidth,  mStokeHeight + ovalPaint.getStrokeWidth() );\n            //画边框遮罩\n            canvas.drawRoundRect(mTextRoundRect, mRoundX, mRoundX, ovalPaint);\n        } else {\n            mTextRoundRect.set(ovalPaint.getStrokeWidth(), ovalPaint.getStrokeWidth(), mStokeWidth + ovalPaint.getStrokeWidth(),  mStokeHeight + ovalPaint.getStrokeWidth());\n\n            canvas.drawCircle(mStokeWidth + lineLong, mCircleY+ ovalPaint.getStrokeWidth() , mRadius, ovalPaint);\n            //画边框遮罩\n            canvas.drawRoundRect(mTextRoundRect, mRoundX, mRoundX, ovalPaint);\n        }\n        canvas.setDrawFilter(pfd);\n        //绘制内圆\n        ovalPaint.setColor(Color.WHITE);\n\n        if (isLeft) {\n            canvas.drawCircle(mRadius, mCircleY+ ovalPaint.getStrokeWidth() , mInnerRadius, ovalPaint);\n\n            //画线\n            canvas.drawLine(mRadius,  mStokeHeight / 2 + ovalPaint.getStrokeWidth(), lineLong + mRadius,  mStokeHeight / 2 + ovalPaint.getStrokeWidth(), ovalPaint);\n\n            mTypeIconRect.set(lineLong + mRadius + leftPadding, mTopPadding +  ovalPaint.getStrokeWidth(), lineLong + mRadius + mIconWidth + leftPadding, mTopPadding + mIconWidth + +  ovalPaint.getStrokeWidth());\n            //画标签类型icon\n            canvas.drawBitmap(mBitmap, null, mTypeIconRect, ovalPaint);\n\n            //文字起始x\n            mTextX = lineLong + mRadius + leftPadding + mIconWidth;\n\n            //文字y\n            Paint.FontMetricsInt fontMetrics = ovalPaint.getFontMetricsInt();\n            mTextY =  (mTextRoundRect.top + mTextRoundRect.bottom - fontMetrics.bottom -\n                    fontMetrics.top) / 2;\n            canvas.drawText(textContent, mTextX, mTextY, ovalPaint);\n\n            //画边框\n            ovalPaint.setStyle(Paint.Style.STROKE);\n            canvas.drawRoundRect(mTextRoundRect, mRoundX, mRoundX, ovalPaint);\n\n        } else {\n            canvas.drawCircle(mStokeWidth + lineLong, mCircleY + ovalPaint.getStrokeWidth(), mInnerRadius, ovalPaint);\n\n            mTypeIconRect.set(leftPadding, mTopPadding + ovalPaint.getStrokeWidth(), mIconWidth + leftPadding, mTopPadding + mIconWidth  +  ovalPaint.getStrokeWidth());\n            //画标签类型icon\n            canvas.drawBitmap(mBitmap, null, mTypeIconRect, ovalPaint);\n\n            //画线\n            canvas.drawLine(mStokeWidth + ovalPaint.getStrokeWidth(),  mStokeHeight / 2 + ovalPaint.getStrokeWidth(), mStokeWidth + lineLong,  mStokeHeight / 2 + ovalPaint.getStrokeWidth(), ovalPaint);\n\n            //文字\n            mTextX = leftPadding + mIconWidth;\n            //文字y\n            Paint.FontMetricsInt fontMetrics = ovalPaint.getFontMetricsInt();\n            mTextY =  (mTextRoundRect.top + mTextRoundRect.bottom - fontMetrics.bottom -\n                    fontMetrics.top) / 2;\n            canvas.drawText(textContent, mTextX, mTextY, ovalPaint);\n\n            //画边框\n            ovalPaint.setStyle(Paint.Style.STROKE);\n\n            canvas.drawRoundRect(mTextRoundRect, mRoundX, mRoundX, ovalPaint);\n        }\n\n        super.onDraw(canvas);\n\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        if(event.getAction() == MotionEvent.ACTION_UP){\n            Log.e(\"zz\",\"无判断条件ACTION_UP\");\n        }\n        if (event.getAction() == MotionEvent.ACTION_UP && mCanMove && mTagGestureListener != null ) {\n            Log.e(\"zz\",\"有判断条件ACTION_UP\");\n            mTagGestureListener.onUp(this, mTagInfoBean);\n\n            float positionX = event.getRawX() - mFirstDownX - tempX;\n            float positionY = event.getRawY() - mFirstDownY - tempY;\n\n            if (positionX < leftBorder) {\n                positionX = leftBorder;\n            } else {\n                float viewWidth = rightBorder - mRadius - lineLong - mStokeWidth;\n                if (positionX > viewWidth) positionX = viewWidth;\n            }\n\n            if (positionY < 0) {\n                positionY = 0;\n            } else {\n                float viewHeight = bottomBorder - mStokeHeight;\n                if (positionY > viewHeight) positionY = viewHeight;\n            }\n\n            //处于删除区域\n            if (deleteRect.contains(positionX, positionY)) {\n                mTagGestureListener.inDeleteRect(TagTextView.this, mTagInfoBean);\n            }\n        }\n        return mGestureDetector.onTouchEvent(event);\n\n    }\n\n\n    public interface TagGestureListener {\n        /**\n         *  手指按下\n         */\n        void onDown(View view, TagInfoBean bean);\n\n        /**\n         *  手指抬起\n         */\n\n        void onUp(View view, TagInfoBean bean);\n\n        /**\n         *  点击了标签\n         */\n        void clickTag(View view, TagInfoBean bean);\n\n        /**\n         *  在删除区域\n         */\n        void inDeleteRect(View view, TagInfoBean bean);\n\n        /**\n         * 在滑动\n         *\n         * @param view\n         * @param bean\n         */\n        void move(View view, TagInfoBean bean);\n    }\n\n    //标签文字内容被点击\n    private TagGestureListener mTagGestureListener;\n\n    public void setTagGestureListener(TagGestureListener tagClickListener) {\n        mTagGestureListener = tagClickListener;\n    }\n\n\n    private GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {\n\n\n        @Override\n        public boolean onDown(MotionEvent e) {\n\n            if (mCanMove && mTagGestureListener != null)\n                mTagGestureListener.onDown(TagTextView.this, mTagInfoBean);\n\n            mFirstDownX = e.getX();\n            mFirstDownY = e.getY();\n\n            //记录父容器和手机屏幕左上角的x和y的值\n            tempX = e.getRawX() - e.getX() - TagTextView.this.getX();\n            tempY = e.getRawY() - e.getY() - TagTextView.this.getY();\n            return mCenterRect.contains(e.getX(), e.getY()) || super.onDown(e);\n        }\n\n\n\n        @Override\n        public boolean onSingleTapUp(MotionEvent e) {\n            //标签被点击\n            if (mCanMove) {\n                //换方向\n                isLeft = !isLeft;\n                isMoveXY = true;\n                //重新记录点的位置\n                meausePercent();\n                invalidate();\n            } else if (mTextRoundRect.contains(e.getX(), e.getY())) {\n                if (!mCanMove && mTagGestureListener != null) { // 不可编辑状态\n                    mTagGestureListener.clickTag(TagTextView.this, mTagInfoBean);\n                }\n            }\n            return true;\n        }\n\n        @Override\n        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {\n\n            //可以滑动编辑\n            if (mCanMove) {\n\n                float positionX = e2.getRawX() - mFirstDownX - tempX;\n                float positionY = e2.getRawY() - mFirstDownY - tempY;\n\n                if (positionX < leftBorder) {\n                    positionX = leftBorder;\n                } else {\n                    float viewWidth = rightBorder - mRadius - lineLong - mStokeWidth;\n                    if (positionX > viewWidth) positionX = viewWidth;\n                }\n\n                if (positionY < 0) {\n                    positionY = 0;\n                } else {\n                    float viewHeight = bottomBorder - mStokeHeight;\n                    if (positionY > viewHeight) positionY = viewHeight;\n                }\n\n                TagTextView.this.setX(positionX);\n                TagTextView.this.setY(positionY);\n                if (mCanMove && mTagGestureListener != null) {\n                    mTagGestureListener.move(TagTextView.this, mTagInfoBean);\n\n                }\n                isMoveXY = true;\n\n                meausePercent();\n\n                return true;\n            } else {\n                return false;\n            }\n\n        }\n\n\n    };\n\n    @Override\n    public boolean dispatchTouchEvent(MotionEvent ev) {\n        //请求所有父控件及祖宗控件不要拦截事件\n        getParent().requestDisallowInterceptTouchEvent(true);\n        return super.dispatchTouchEvent(ev);\n    }\n\n\n    /**\n     * 计算圆心的坐标占比\n     */\n    private void meausePercent() {\n        if (parentWidth == 0 || parentHeight == 0) return;\n        if (isLeft) {\n            circleX = (int) (getX() + mRadius);\n            circleY = (int) (getY() + mStokeHeight / 2 + ovalPaint.getStrokeWidth());\n        } else {\n            circleX = (int) (getX() + mStokeWidth + lineLong + ovalPaint.getStrokeWidth() );\n            circleY = (int) (getY() + mStokeHeight / 2 + ovalPaint.getStrokeWidth());\n        }\n        percentX = (((float) circleX) / parentWidth);\n        percentY = (((float) circleY) / parentHeight);\n\n\n\n        Log.e(\"zz\", \"圆点相关数据\");\n        Log.e(\"zz\", \"圆点坐标 x == \"+ circleX +\"  , y == \" + circleY );\n        Log.e(\"zz\", \"圆点在图片上的坐标比例  x  == \"+ percentX +\"  , y == \" + percentY );\n        Log.e(\"zz\", \"圆点数据：\"+ getTagInfoBean().toString() );\n    }\n\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/ken/tagimage/ViewDialogFragment.java",
    "content": "package com.ken.tagimage;\n\nimport android.app.Dialog;\nimport android.content.DialogInterface;\nimport android.os.Bundle;\nimport android.support.v4.app.DialogFragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v7.app.AlertDialog;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.EditText;\n\npublic class ViewDialogFragment extends DialogFragment {\n\n    public interface Callback {\n        void onClick(String tabName);\n    }\n\n    private Callback callback;\n\n    public void show(FragmentManager fragmentManager) {\n        show(fragmentManager, \"ViewDialogFragment\");\n    }\n\n    @Override\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());\n        LayoutInflater inflater = getActivity().getLayoutInflater();\n\n        final View view = inflater.inflate(R.layout.dialog_add_tag, null);\n        final EditText tab_name = (EditText) view.findViewById(R.id.tab_name);\n        builder.setView(view)\n                .setPositiveButton(\"确定\", new DialogInterface.OnClickListener() {\n                    @Override\n                    public void onClick(DialogInterface dialog, int which) {\n                        if (callback != null) {\n                            callback.onClick(tab_name.getText().toString());\n                        }\n                    }\n                })\n        ;\n        return builder.create();\n    }\n\n    public void setCallback(Callback callback) {\n        this.callback = callback;\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        callback = null;\n    }\n}"
  },
  {
    "path": "app/src/main/res/drawable/ic_android_black_24dp.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:width=\"24dp\"\n        android:height=\"24dp\"\n        android:viewportWidth=\"24.0\"\n        android:viewportHeight=\"24.0\">\n    <path\n        android:fillColor=\"#FF000000\"\n        android:pathData=\"M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z\"/>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"78.5885\"\n                android:endY=\"90.9159\"\n                android:startX=\"48.7653\"\n                android:startY=\"61.0927\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#ffffff\"\n    tools:context=\"com.ken.tagimage.MainActivity\">\n    <ImageView\n        android:id=\"@+id/image\"\n        android:src=\"@mipmap/girl\"\n        android:scaleType=\"centerCrop\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n    <com.ken.tagimage.TagImageView\n        android:id=\"@+id/tag_content\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/>\n    <TextView\n        android:layout_marginBottom=\"50dp\"\n        android:layout_gravity=\"center_horizontal|bottom\"\n        android:textColor=\"@color/colorAccent\"\n        android:text=\"点击图片任意位置可添加标签\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_add_tag.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"300dp\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\">\n\n\n    <EditText\n        android:layout_gravity=\"center\"\n        android:id=\"@+id/tab_name\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"4dp\"\n        android:layout_marginLeft=\"4dp\"\n        android:layout_marginRight=\"4dp\"\n        android:layout_marginTop=\"16dp\"\n        android:hint=\"标签名称\"\n        />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/image_tag.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\">\n\n\n    <RelativeLayout\n        android:id=\"@+id/tagsGroup\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"/>\n\n\n    <ImageView\n        android:id=\"@+id/delete_tags\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginBottom=\"50dp\"\n        android:src=\"@mipmap/delete_tag\"\n        android:layout_width=\"30dp\"\n        android:layout_height=\"30dp\"/>\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">TagImage</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/test/java/com/ken/tagimage/ExampleUnitTest.java",
    "content": "package com.ken.tagimage;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    \n    repositories {\n        google()\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.0.1'\n        \n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        jcenter()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Tue Aug 14 13:35:27 CST 2018\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.1-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\n"
  }
]