Repository: GIVEWAYTO/TagImageView Branch: master Commit: b6b021164e46 Files: 36 Total size: 66.2 KB Directory structure: gitextract_c4rl830d/ ├── .gitignore ├── .idea/ │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ └── runConfigurations.xml ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── ken/ │ │ └── tagimage/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── ken/ │ │ │ └── tagimage/ │ │ │ ├── Density.java │ │ │ ├── MainActivity.java │ │ │ ├── TagImageView.java │ │ │ ├── TagInfoBean.java │ │ │ ├── TagTextView.java │ │ │ └── ViewDialogFragment.java │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── ic_android_black_24dp.xml │ │ │ └── ic_launcher_background.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_main.xml │ │ │ ├── dialog_add_tag.xml │ │ │ └── image_tag.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── com/ │ └── ken/ │ └── tagimage/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures .externalNativeBuild ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: README.md ================================================ # 高仿小红书之标签添加功能
  1. 随点击处添加标签
  2. 计算标签位置
  3. 可将标签位置还原渲染至不同屏幕尺寸
  4. 拖拽删除标签
  5. 可拖拽时支持点击标签更换文字方向
  6. 不可拖拽时支持点击标签响应点击事件
未做的: 当标签贴边,文字框将会收缩。 ## 效果图 ![](video.gif) ### Log ![](log.jpg) ## 圆点相关数据 圆点坐标 x == 348 , y == 825 圆点在图片上的坐标百分比% x == 0.32222223 , y == 0.5729167 圆点数据: TagInfoBean{ name='¥55 粉色衣服', notesTagType=3, url='tag点的链接url', x=0.3222222328186035, y=0.5729166865348816, width=1080.0, height=1440.0, picWidth=1010.0, picHeight=1324.0, notesTagId=652, isLeft=true, isCanMove=true, index=1 } ## Bean private String name; //标签内容 private int notesTagType; //标签type private String url; //标签url private double x; //圆心x的在父控件位置 % private double y; //圆心y的在父控件位置 % private float width; //控件宽度 private float height; //控件高度 private float picWidth; //图片的宽度 private float picHeight; //图片的高度 private int notesTagId; //标签id private boolean isLeft = true; //圆点是否在左边 private boolean isCanMove = true; //标签是否可以移动 private int index; //用来记录在编辑标签中的index 位置 ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.ken.tagimage" minSdkVersion 16 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:27+' implementation 'com.android.support.constraint:constraint-layout:1.1.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: app/src/androidTest/java/com/ken/tagimage/ExampleInstrumentedTest.java ================================================ package com.ken.tagimage; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.ken.tagimage", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/ken/tagimage/Density.java ================================================ /* * Copyright (c) 2014,KJFrameForAndroid Open Source Project,张涛. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ken.tagimage; import android.content.Context; import android.util.DisplayMetrics; /** * 作者: by KEN on 2018/7/14 17. * 邮箱: gr201655@163.com */ public final class Density { /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } public static float dip2pxForFloat(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (dpValue * scale + 0.5f); } /** * 根据手机的分辨率从 px(像素) 的单位 转成为 dp */ public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } /** * 根据手机的分辨率从 px(像素) 的单位 转成为 sp */ public static int px2sp(Context context, float pxValue) { float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (pxValue / fontScale + 0.5f); } /** * 根据手机的分辨率从 sp 的单位 转成为 px */ public static int sp2px(Context context, float spValue) { float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } /** * 获取dialog宽度 */ public static int getDialogW(Context aty) { DisplayMetrics dm = new DisplayMetrics(); dm = aty.getResources().getDisplayMetrics(); int w = dm.widthPixels - 100; // int w = aty.getWindowManager().getDefaultDisplay().getWidth() - 100; return w; } /** * 获取屏幕宽度 */ public static int getScreenW(Context aty) { DisplayMetrics dm = new DisplayMetrics(); dm = aty.getResources().getDisplayMetrics(); int w = dm.widthPixels; // int w = aty.getWindowManager().getDefaultDisplay().getWidth(); return w; } /** * 获取屏幕高度 */ public static int getScreenH(Context aty) { DisplayMetrics dm = new DisplayMetrics(); dm = aty.getResources().getDisplayMetrics(); int h = dm.heightPixels; // int h = aty.getWindowManager().getDefaultDisplay().getHeight(); return h; } } ================================================ FILE: app/src/main/java/com/ken/tagimage/MainActivity.java ================================================ package com.ken.tagimage; import android.content.pm.ActivityInfo; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.util.Log; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.Toast; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private int imageH; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); requestWindowFeature(Window.FEATURE_NO_TITLE); //禁止横屏 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); setContentView(R.layout.activity_main); final TagImageView tag_view = findViewById(R.id.tag_content); tag_view.setClickTagListener(new TagImageView.ClickTagListener() { @Override public void click(TagInfoBean bean) { Toast.makeText(MainActivity.this, "标签被点击 == " + bean.getName(), Toast.LENGTH_SHORT).show(); } }); //添加标签 tag_view.setAddTagListener(new TagImageView.AddTagListener() { @Override public void addTag(String path, double rawX, double rawY) { final TagInfoBean bean = new TagInfoBean(); bean.setCanMove(true); bean.setNotesTagId(652); bean.setNotesTagType(TagTextView.TAG_TEXT); //通过手机中的图片地址 或者 网络拉取的图片信息 获得图片宽高 bean.setPicWidth(1010); bean.setPicHeight(1324); bean.setUrl("tag点的链接url"); // 显示控件的显示 依照图片的本身的宽高比例进行动态设置 bean.setWidth(Density.getScreenW(MainActivity.this)); //标签在控件上的比例 bean.setLeft(!(rawX > bean.getWidth()/ 2)); bean.setX(rawX / bean.getWidth()); bean.setY(rawY / imageH); bean.setHeight(imageH); ViewDialogFragment dialogFragment = new ViewDialogFragment(); dialogFragment.setCallback(new ViewDialogFragment.Callback() { @Override public void onClick(String tabName) { if (TextUtils.isEmpty(tabName)) tabName = "女孩"; bean.setName(tabName); Log.e("zz", "onClick: "+bean.getName() + " " + imageH ); tag_view.addTag(bean); } }); dialogFragment.show(getSupportFragmentManager()); } }); //删除标签 tag_view.setDeleteTagListener(new TagImageView.DeleteTagListener() { @Override public void remove(String path, TagInfoBean bean) { Toast.makeText(MainActivity.this, "删除标签 == " + bean.getName(), Toast.LENGTH_SHORT).show(); } }); //设置图片的路径 tag_view.setPath("一般是本地图片地址,这里用的是资源图片"); //可用来标记这些标签属于哪张图片 //添加初始标签 List tagInfoBeanList = new ArrayList<>(); tagInfoBeanList.add(createTag1()); tagInfoBeanList.add(createTag2()); tagInfoBeanList.add(createTag3()); //设置 图片的高度 可根据实际的图片高度比例 设置 FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, imageH); findViewById(R.id.image).setLayoutParams(params); tag_view.setLayoutParams(params); tag_view.setTagList(tagInfoBeanList); } @NonNull private TagInfoBean createTag1() { TagInfoBean bean = new TagInfoBean(); //该标签是否可以移动 bean.setCanMove(false); bean.setLeft(false); bean.setName("一杯奶茶"); bean.setNotesTagId(652); bean.setNotesTagType(TagTextView.TAG_BRAND); //通过手机中的图片地址 或者 网络拉取的图片信息 获得图片宽高 bean.setPicWidth(1010); bean.setPicHeight(1324); bean.setUrl("tag点的链接url"); // 显示控件的显示 依照图片的本身的宽高比例进行动态设置 bean.setWidth(Density.getScreenW(this)); imageH = 0; //项目中需求是只有1:1 和 3:4的比例 这个可根据实际修改 直接按图片比例也可以 if (bean.getPicWidth() / bean.getPicHeight() > 0.85f) { imageH = (int) bean.getWidth(); } else { imageH = (int) (bean.getWidth() * 4 / 3); } //标签原点在照片上的比例 bean.setX(0.7513889); bean.setY(0.5864583); bean.setHeight(imageH); return bean; } @NonNull private TagInfoBean createTag2() { TagInfoBean bean = new TagInfoBean(); bean.setCanMove(true); bean.setLeft(true); bean.setName("¥55 粉色衣服"); bean.setNotesTagId(652); bean.setNotesTagType(TagTextView.TAG_PRICE); //通过手机中的图片地址 或者 网络拉取的图片信息 获得图片宽高 bean.setPicWidth(1010); bean.setPicHeight(1324); bean.setUrl("tag点的链接url"); // 显示控件的显示 依照图片的本身的宽高比例进行动态设置 bean.setWidth(Density.getScreenW(this)); imageH = 0; //项目中需求是只有1:1 和 3:4的比例 这个可根据实际修改 直接按图片比例也可以 if (bean.getPicWidth() / bean.getPicHeight() > 0.85f) { imageH = (int) bean.getWidth(); } else { imageH = (int) (bean.getWidth() * 4 / 3); } //标签原点在照片上的比例 bean.setX(0.5625); bean.setY(0.81041664); bean.setHeight(imageH); return bean; } @NonNull private TagInfoBean createTag3() { TagInfoBean bean = new TagInfoBean(); bean.setCanMove(true); bean.setLeft(true); bean.setName("大眼睛"); bean.setNotesTagId(652); bean.setNotesTagType(TagTextView.TAG_BRAND); //通过手机中的图片地址 或者 网络拉取的图片信息 获得图片宽高 bean.setPicWidth(1010); bean.setPicHeight(1324); bean.setUrl("tag点的链接url"); // 显示控件的显示 依照图片的本身的宽高比例进行动态设置 bean.setWidth(Density.getScreenW(this)); imageH = 0; //计算图片的高度 因为项目中需求是图片只有1:1 和 3:4的比例 这个可根据实际修改 直接按图片比例也可以 if (bean.getPicWidth() / bean.getPicHeight() > 0.85f) { imageH = (int) bean.getWidth(); } else { imageH = (int) (bean.getWidth() * 4 / 3); } //标签原点在照片上的比例 bean.setX(0.35833332); bean.setY(0.29583332); bean.setHeight(imageH); return bean; } } ================================================ FILE: app/src/main/java/com/ken/tagimage/TagImageView.java ================================================ package com.ken.tagimage; import android.content.Context; import android.graphics.Paint; import android.graphics.Rect; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.animation.CycleInterpolator; import android.widget.FrameLayout; import android.widget.RelativeLayout; import java.util.ArrayList; import java.util.List; /** * 作者: by KEN on 2018/7/14 17. * 邮箱: gr201655@163.com */ public class TagImageView extends FrameLayout { private RelativeLayout mContentLayout; private Paint mOvalPaint; private float topPadding; private int leftPading; private int mRadius; private int lineLong; private View mDelete_tags; private TagTextView.TagGestureListener tagClickListener; private boolean isClick = false; private String mPath; /** * 标签数据 最后只要重新设置每个标签当前的位置 * 标签有可能被编辑 所以要获取最后的位置 即可 * 如果标签是不可移动的属性 则不需要更新 */ private List infoBeanList = new ArrayList<>(); public TagImageView(Context context) { this(context, null); } public TagImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TagImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } private void initView(Context context) { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.image_tag, this, true); mDelete_tags = findViewById(R.id.delete_tags); mDelete_tags.setVisibility(View.GONE); mContentLayout = (RelativeLayout) findViewById(R.id.tagsGroup); mOvalPaint = new Paint(); mOvalPaint.setTextSize(Density.sp2px(context, 14)); mOvalPaint.setStrokeWidth(Density.dip2px(context, 1)); mOvalPaint.setFilterBitmap(true); //滑动 mContentLayout.setOnTouchListener(onTouchListener); //边框与文字顶部底部的距离 topPadding = Density.dip2px(context, 3); leftPading = Density.dip2px(context, 6); mRadius = Density.dip2px(context, 4); //线长 lineLong = Density.dip2px(context, 15); //标签手势监听 tagClickListener = new TagTextView.TagGestureListener() { @Override public void onDown(View view, TagInfoBean bean) { if (mDelete_tags.getVisibility() == View.GONE && bean.isCanMove()) { mDelete_tags.setVisibility(View.VISIBLE); } } @Override public void onUp(View view, TagInfoBean bean) { if (mDelete_tags.getVisibility() == View.VISIBLE) { mDelete_tags.setVisibility(View.GONE); } } @Override public void clickTag(View view, TagInfoBean bean) { if(mClickTagListener!=null) mClickTagListener.click(bean); } @Override public void inDeleteRect(View view, TagInfoBean bean) { mContentLayout.removeView(view); infoBeanList.remove(bean); //移除的监听 if (mDeleteTagListener != null) mDeleteTagListener.remove(mPath, bean); } @Override public void move(View view, TagInfoBean bean) { } }; } float mLastX; float downX; float downY; OnTouchListener onTouchListener = new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downX = event.getRawX(); downY = event.getRawY(); mLastX = event.getX(); isClick = true; break; case MotionEvent.ACTION_UP: if (isClick) { double rawX = event.getRawX(); double rawY = event.getRawY(); mAddTagListener.addTag(mPath, downX, downY); } break; case MotionEvent.ACTION_MOVE: if (isClick && Math.abs(event.getX() - mLastX) > 50f) { isClick = false; } return false; } return true; } }; public void addTag(TagInfoBean infoBean) { infoBeanList.add(infoBean); createTags(infoBean); } public void setAddTagListener(AddTagListener addTagListener) { mAddTagListener = addTagListener; } public void setDeleteTagListener(DeleteTagListener deleteTagListener) { mDeleteTagListener = deleteTagListener; } /** * 添加标签 */ private AddTagListener mAddTagListener; public interface AddTagListener { void addTag(String path, double rawX, double rawY); } /** * 删除标签 */ private DeleteTagListener mDeleteTagListener; public interface DeleteTagListener { void remove(String path, TagInfoBean bean); } /** * 标签被点击 */ private ClickTagListener mClickTagListener; public void setClickTagListener(ClickTagListener mClickTagListener) { this.mClickTagListener = mClickTagListener; } public interface ClickTagListener { void click(TagInfoBean bean); } /** * 图片在本地的地址 * * @param path */ public void setPath(String path) { mPath = path; } public String getPath() { return mPath; } public void setTagList(List list) { clearTags(); int num = 0;//用于计数 infoBeanList = list; for (TagInfoBean bean : list) { if (TextUtils.isEmpty(bean.getName())) { continue; } bean.setIndex(num++); createTags(bean); } } private void createTags(TagInfoBean bean) { //获取文本的宽高 final Rect bounds = new Rect(); String name = bean.getName(); if (name.length() > 16) { name = name.substring(0, 16) + "..."; } mOvalPaint.getTextBounds(name, 0, name.length(), bounds); //获得边框的宽高 final float mStokeHeight = bounds.bottom - bounds.top + topPadding * 2; int left = 0; int top = 0; top = (int) (bean.getHeight() * bean.getY() - mStokeHeight / 2); if (bean.isLeft()) { left = (int) (bean.getWidth() * bean.getX() - mRadius); } else { int mStokeWidth = bounds.right - bounds.left + leftPading * 2 + (bounds.bottom - bounds.top); left = (int) (bean.getWidth() * bean.getX() - mStokeWidth - lineLong); } RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout .LayoutParams .WRAP_CONTENT); params.setMargins(left, top, 0, 0); final TagTextView child = new TagTextView(getContext(), bean); child.setTagGestureListener(tagClickListener); mContentLayout.addView(child, params); } private void clearTags() { mContentLayout.removeAllViews(); } } ================================================ FILE: app/src/main/java/com/ken/tagimage/TagInfoBean.java ================================================ package com.ken.tagimage; /** * 作者: by KEN on 2018/7/14 17. * 邮箱: gr201655@163.com * * 由于项目为统一跟后台定义字段相同 有部分命名不准确 */ public class TagInfoBean { private String name; //标签内容 private int notesTagType; //标签type private String url; //标签url private double x; //圆心x的在父控件位置 % private double y; //圆心y的在父控件位置 % private float width; //控件宽度 private float height; //控件高度 private float picWidth; //图片的宽度 private float picHeight; //图片的高度 private int notesTagId; //标签id private boolean isLeft = true; //圆点是否在左边 private boolean isCanMove = true; //标签是否可以移动 private int index; //用来记录在编辑标签中的index 位置 public void setCanMove(boolean canMove) { isCanMove = canMove; } public boolean isCanMove() { return isCanMove; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getNotesTagType() { return notesTagType; } public void setNotesTagType(int notesTagType) { this.notesTagType = notesTagType; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public double getX() { return x; } public void setX(double x) { this.x = x; } public float getPicWidth() { return picWidth; } public void setPicWidth(float picWidth) { this.picWidth = picWidth; } public float getPicHeight() { return picHeight; } public void setPicHeight(float picHeight) { this.picHeight = picHeight; } public double getY() { return y; } public void setY(double y) { this.y = y; } public float getWidth() { return width; } public void setWidth(float width) { this.width = width; } public float getHeight() { return height; } public void setHeight(float height) { this.height = height; } public int getNotesTagId() { return notesTagId; } public void setNotesTagId(int notesTagId) { this.notesTagId = notesTagId; } public boolean isLeft() { return isLeft; } public void setLeft(boolean left) { isLeft = left; } public void setIndex(int index) { this.index = index; } public int getIndex() { return index; } @Override public String toString() { return "TagInfoBean{" + "name='" + name + '\'' + ", notesTagType=" + notesTagType + ", url='" + url + '\'' + ", x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + ", picWidth=" + picWidth + ", picHeight=" + picHeight + ", notesTagId=" + notesTagId + ", isLeft=" + isLeft + ", isCanMove=" + isCanMove + ", index=" + index + '}'; } } ================================================ FILE: app/src/main/java/com/ken/tagimage/TagTextView.java ================================================ package com.ken.tagimage; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.Rect; import android.graphics.RectF; import android.support.v4.view.GestureDetectorCompat; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; /** * 作者: by KEN on 2018/7/14 17. * 邮箱: gr201655@163.com */ public class TagTextView extends View { private Paint ovalPaint = new Paint(); private float mRadius = 0; //外圆半径 private float mInnerRadius = 0; //内圆半径 private RectF mCenterRect; private int lineLong = 0; //横线的长度 private RectF mTextRoundRect; //文字框内容高度 private String textContent; //文本内容 private final int TextMaxNum = 16; //文本最多显示16个文字 private GestureDetectorCompat mGestureDetector; private float mStokeHeight; //边框的高度 private float mStokeWidth; //边框的宽度 private float mCircleY; //中心圆的高度 private float leftPadding; private float mTextX; //文字起始x private float mTextY; //文字baseline的高度 private int mRoundX; private float mFirstDownX; private float mFirstDownY; private int leftBorder = 0; //上边界 左边界 private int rightBorder = 0; //右边界 private int bottomBorder = 0; //底部边界 private int circleX = 0; //中心圆的x坐标 private int circleY = 0; //中心圆的y坐标 private int parentWidth = 0; //父级的宽度 private int parentHeight = 0; //父级的高度 private float percentX = 0f; //在父级的宽度占比 private float percentY = 0f; //在父级的高度占比 private boolean isLeft = true; //圆点是否在左边 private TagInfoBean mTagInfoBean; private int mShadeColor; public static final int TAG_TEXT = 0; //一般 public static final int TAG_LOCATION = 1; //地址 public static final int TAG_BRAND = 2; //品牌 public static final int TAG_PRICE = 3; //价格 private boolean mCanMove = true; //可以被移动 private float tempX; // 临时记录按键按下的位置 private float tempY; private Bitmap mBitmap; private float mTopPadding; private RectF mTypeIconRect; private int mIconWidth; private boolean isMoveXY = false; // 用来记录是否被移动过 如果没有则不改变数据中的 xy百分比 private RectF deleteRect; //用来判断是否滑动到底部 private PaintFlagsDrawFilter pfd; public TagInfoBean getTagInfoBean() { if (isMoveXY) { mTagInfoBean.setX(percentX); mTagInfoBean.setY(percentY); } return mTagInfoBean; } public TagTextView(Context context, TagInfoBean tagInfoBean) { this(context, null, 0); mTagInfoBean = tagInfoBean; initView(context); } public TagTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TagTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } private void initView(Context context) { if (mTagInfoBean == null) return; //在1+手机滑动时出现了残影 也有可能是硬件加速的问题 // setFadingEdgeLength(0); // setLayerType(View.LAYER_TYPE_SOFTWARE, null); ovalPaint.setAntiAlias(true); ovalPaint.setStyle(Paint.Style.FILL); ovalPaint.setColor(Color.WHITE); ovalPaint.setStrokeWidth(Density.dip2px(context, 1)); mTextRoundRect = new RectF(); mShadeColor = Color.parseColor("#30ffffff"); mRadius = Density.dip2px(context, 4); mInnerRadius = Density.dip2px(context, 2.5f); BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inPreferredConfig = Bitmap.Config.ARGB_8888; switch (mTagInfoBean.getNotesTagType()) { case TAG_LOCATION: mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.tags_location, opts); break; case TAG_BRAND: mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.brand, opts); break; case TAG_PRICE: mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.price, opts); break; default: mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.tags_text, opts); break; } //防止bitmap 变模糊 ovalPaint.setFilterBitmap(true); ovalPaint.setAntiAlias(true); ovalPaint.setStrokeWidth(Density.dip2px(getContext(),1)); pfd = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); ovalPaint.setTextSize(Density.dip2px(context, 14)); lineLong = Density.dip2px(context, 15); textContent = mTagInfoBean.getName(); if (TextUtils.isEmpty(textContent)) return; if (textContent.length() > TextMaxNum) { textContent = textContent.substring(0, TextMaxNum - 1) + "..."; } mCanMove = mTagInfoBean.isCanMove(); rightBorder = Density.getScreenW(context); //获取文本的宽高 Rect bounds = new Rect(); ovalPaint.getTextBounds(textContent, 0, textContent.length(), bounds); //边框与文字顶部底部的距离 mTopPadding = Density.dip2px(context, 3); //边框与文字左右的距离 leftPadding = Density.dip2px(context, 6); isLeft = mTagInfoBean.isLeft(); //标签类别icon mTypeIconRect = new RectF(); int iconHeight = bounds.bottom - bounds.top; mIconWidth = iconHeight; mTypeIconRect.set(lineLong + mRadius + leftPadding, mTopPadding, lineLong + mRadius + mIconWidth + leftPadding, mTopPadding + iconHeight); //获得边框的宽高 mStokeHeight = bounds.bottom - bounds.top + mTopPadding * 2; mStokeWidth = bounds.right - bounds.left + leftPadding * 2 + mIconWidth; //文字Rect的大小 mTextRoundRect.set(lineLong + mRadius, 0, lineLong + mRadius + mStokeWidth, mStokeHeight); //圆心y mCircleY = mStokeHeight / 2; //文字起始x mTextX = lineLong + mRadius + leftPadding + mIconWidth; //文字y Paint.FontMetricsInt fontMetrics = ovalPaint.getFontMetricsInt(); mTextY = (mTextRoundRect.top + mTextRoundRect.bottom - fontMetrics.bottom - fontMetrics.top) / 2; //整个的控件的位置 mCenterRect = new RectF(); mCenterRect.set(0, 0, mRadius + lineLong + mStokeWidth, mStokeHeight); //手势监听 mGestureDetector = new GestureDetectorCompat(context, mGestureListener); mGestureDetector.setIsLongpressEnabled(false); parentWidth = rightBorder; parentHeight = (int) mTagInfoBean.getHeight(); bottomBorder = parentHeight; //删除--控件的半径 int delet_raduis = Density.dip2px(context, 15); float top = parentHeight - Density.dip2px(context, 80) - mStokeHeight / 2; //删除图标的大小 deleteRect = new RectF(rightBorder / 2 - (lineLong + mStokeWidth + delet_raduis), top, rightBorder / 2 + delet_raduis, parentHeight - Density.dip2px(context, 50) - mStokeHeight / 2); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (mRadius + lineLong + mStokeWidth +ovalPaint.getStrokeWidth() * 2), MeasureSpec.EXACTLY); heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (mStokeHeight + ovalPaint.getStrokeWidth() * 2), MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //边框圆角半径 mRoundX = h / 2; } @Override protected void onDraw(Canvas canvas) { //绘制外圆 ovalPaint.setStyle(Paint.Style.FILL); ovalPaint.setColor(mShadeColor); //原点在左边 if (isLeft) { canvas.drawCircle(mRadius, mCircleY + ovalPaint.getStrokeWidth(), mRadius, ovalPaint); mTextRoundRect.set(lineLong + mRadius, ovalPaint.getStrokeWidth(), lineLong + mRadius + mStokeWidth, mStokeHeight + ovalPaint.getStrokeWidth() ); //画边框遮罩 canvas.drawRoundRect(mTextRoundRect, mRoundX, mRoundX, ovalPaint); } else { mTextRoundRect.set(ovalPaint.getStrokeWidth(), ovalPaint.getStrokeWidth(), mStokeWidth + ovalPaint.getStrokeWidth(), mStokeHeight + ovalPaint.getStrokeWidth()); canvas.drawCircle(mStokeWidth + lineLong, mCircleY+ ovalPaint.getStrokeWidth() , mRadius, ovalPaint); //画边框遮罩 canvas.drawRoundRect(mTextRoundRect, mRoundX, mRoundX, ovalPaint); } canvas.setDrawFilter(pfd); //绘制内圆 ovalPaint.setColor(Color.WHITE); if (isLeft) { canvas.drawCircle(mRadius, mCircleY+ ovalPaint.getStrokeWidth() , mInnerRadius, ovalPaint); //画线 canvas.drawLine(mRadius, mStokeHeight / 2 + ovalPaint.getStrokeWidth(), lineLong + mRadius, mStokeHeight / 2 + ovalPaint.getStrokeWidth(), ovalPaint); mTypeIconRect.set(lineLong + mRadius + leftPadding, mTopPadding + ovalPaint.getStrokeWidth(), lineLong + mRadius + mIconWidth + leftPadding, mTopPadding + mIconWidth + + ovalPaint.getStrokeWidth()); //画标签类型icon canvas.drawBitmap(mBitmap, null, mTypeIconRect, ovalPaint); //文字起始x mTextX = lineLong + mRadius + leftPadding + mIconWidth; //文字y Paint.FontMetricsInt fontMetrics = ovalPaint.getFontMetricsInt(); mTextY = (mTextRoundRect.top + mTextRoundRect.bottom - fontMetrics.bottom - fontMetrics.top) / 2; canvas.drawText(textContent, mTextX, mTextY, ovalPaint); //画边框 ovalPaint.setStyle(Paint.Style.STROKE); canvas.drawRoundRect(mTextRoundRect, mRoundX, mRoundX, ovalPaint); } else { canvas.drawCircle(mStokeWidth + lineLong, mCircleY + ovalPaint.getStrokeWidth(), mInnerRadius, ovalPaint); mTypeIconRect.set(leftPadding, mTopPadding + ovalPaint.getStrokeWidth(), mIconWidth + leftPadding, mTopPadding + mIconWidth + ovalPaint.getStrokeWidth()); //画标签类型icon canvas.drawBitmap(mBitmap, null, mTypeIconRect, ovalPaint); //画线 canvas.drawLine(mStokeWidth + ovalPaint.getStrokeWidth(), mStokeHeight / 2 + ovalPaint.getStrokeWidth(), mStokeWidth + lineLong, mStokeHeight / 2 + ovalPaint.getStrokeWidth(), ovalPaint); //文字 mTextX = leftPadding + mIconWidth; //文字y Paint.FontMetricsInt fontMetrics = ovalPaint.getFontMetricsInt(); mTextY = (mTextRoundRect.top + mTextRoundRect.bottom - fontMetrics.bottom - fontMetrics.top) / 2; canvas.drawText(textContent, mTextX, mTextY, ovalPaint); //画边框 ovalPaint.setStyle(Paint.Style.STROKE); canvas.drawRoundRect(mTextRoundRect, mRoundX, mRoundX, ovalPaint); } super.onDraw(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_UP){ Log.e("zz","无判断条件ACTION_UP"); } if (event.getAction() == MotionEvent.ACTION_UP && mCanMove && mTagGestureListener != null ) { Log.e("zz","有判断条件ACTION_UP"); mTagGestureListener.onUp(this, mTagInfoBean); float positionX = event.getRawX() - mFirstDownX - tempX; float positionY = event.getRawY() - mFirstDownY - tempY; if (positionX < leftBorder) { positionX = leftBorder; } else { float viewWidth = rightBorder - mRadius - lineLong - mStokeWidth; if (positionX > viewWidth) positionX = viewWidth; } if (positionY < 0) { positionY = 0; } else { float viewHeight = bottomBorder - mStokeHeight; if (positionY > viewHeight) positionY = viewHeight; } //处于删除区域 if (deleteRect.contains(positionX, positionY)) { mTagGestureListener.inDeleteRect(TagTextView.this, mTagInfoBean); } } return mGestureDetector.onTouchEvent(event); } public interface TagGestureListener { /** * 手指按下 */ void onDown(View view, TagInfoBean bean); /** * 手指抬起 */ void onUp(View view, TagInfoBean bean); /** * 点击了标签 */ void clickTag(View view, TagInfoBean bean); /** * 在删除区域 */ void inDeleteRect(View view, TagInfoBean bean); /** * 在滑动 * * @param view * @param bean */ void move(View view, TagInfoBean bean); } //标签文字内容被点击 private TagGestureListener mTagGestureListener; public void setTagGestureListener(TagGestureListener tagClickListener) { mTagGestureListener = tagClickListener; } private GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDown(MotionEvent e) { if (mCanMove && mTagGestureListener != null) mTagGestureListener.onDown(TagTextView.this, mTagInfoBean); mFirstDownX = e.getX(); mFirstDownY = e.getY(); //记录父容器和手机屏幕左上角的x和y的值 tempX = e.getRawX() - e.getX() - TagTextView.this.getX(); tempY = e.getRawY() - e.getY() - TagTextView.this.getY(); return mCenterRect.contains(e.getX(), e.getY()) || super.onDown(e); } @Override public boolean onSingleTapUp(MotionEvent e) { //标签被点击 if (mCanMove) { //换方向 isLeft = !isLeft; isMoveXY = true; //重新记录点的位置 meausePercent(); invalidate(); } else if (mTextRoundRect.contains(e.getX(), e.getY())) { if (!mCanMove && mTagGestureListener != null) { // 不可编辑状态 mTagGestureListener.clickTag(TagTextView.this, mTagInfoBean); } } return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { //可以滑动编辑 if (mCanMove) { float positionX = e2.getRawX() - mFirstDownX - tempX; float positionY = e2.getRawY() - mFirstDownY - tempY; if (positionX < leftBorder) { positionX = leftBorder; } else { float viewWidth = rightBorder - mRadius - lineLong - mStokeWidth; if (positionX > viewWidth) positionX = viewWidth; } if (positionY < 0) { positionY = 0; } else { float viewHeight = bottomBorder - mStokeHeight; if (positionY > viewHeight) positionY = viewHeight; } TagTextView.this.setX(positionX); TagTextView.this.setY(positionY); if (mCanMove && mTagGestureListener != null) { mTagGestureListener.move(TagTextView.this, mTagInfoBean); } isMoveXY = true; meausePercent(); return true; } else { return false; } } }; @Override public boolean dispatchTouchEvent(MotionEvent ev) { //请求所有父控件及祖宗控件不要拦截事件 getParent().requestDisallowInterceptTouchEvent(true); return super.dispatchTouchEvent(ev); } /** * 计算圆心的坐标占比 */ private void meausePercent() { if (parentWidth == 0 || parentHeight == 0) return; if (isLeft) { circleX = (int) (getX() + mRadius); circleY = (int) (getY() + mStokeHeight / 2 + ovalPaint.getStrokeWidth()); } else { circleX = (int) (getX() + mStokeWidth + lineLong + ovalPaint.getStrokeWidth() ); circleY = (int) (getY() + mStokeHeight / 2 + ovalPaint.getStrokeWidth()); } percentX = (((float) circleX) / parentWidth); percentY = (((float) circleY) / parentHeight); Log.e("zz", "圆点相关数据"); Log.e("zz", "圆点坐标 x == "+ circleX +" , y == " + circleY ); Log.e("zz", "圆点在图片上的坐标比例 x == "+ percentX +" , y == " + percentY ); Log.e("zz", "圆点数据:"+ getTagInfoBean().toString() ); } } ================================================ FILE: app/src/main/java/com/ken/tagimage/ViewDialogFragment.java ================================================ package com.ken.tagimage; import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentManager; import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; public class ViewDialogFragment extends DialogFragment { public interface Callback { void onClick(String tabName); } private Callback callback; public void show(FragmentManager fragmentManager) { show(fragmentManager, "ViewDialogFragment"); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); LayoutInflater inflater = getActivity().getLayoutInflater(); final View view = inflater.inflate(R.layout.dialog_add_tag, null); final EditText tab_name = (EditText) view.findViewById(R.id.tab_name); builder.setView(view) .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (callback != null) { callback.onClick(tab_name.getText().toString()); } } }) ; return builder.create(); } public void setCallback(Callback callback) { this.callback = callback; } @Override public void onDestroy() { super.onDestroy(); callback = null; } } ================================================ FILE: app/src/main/res/drawable/ic_android_black_24dp.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================ ================================================ FILE: app/src/main/res/layout/dialog_add_tag.xml ================================================ ================================================ FILE: app/src/main/res/layout/image_tag.xml ================================================ ================================================ FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml ================================================ ================================================ FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml ================================================ ================================================ FILE: app/src/main/res/values/colors.xml ================================================ #3F51B5 #303F9F #FF4081 ================================================ FILE: app/src/main/res/values/strings.xml ================================================ TagImage ================================================ FILE: app/src/main/res/values/styles.xml ================================================ ================================================ FILE: app/src/test/java/com/ken/tagimage/ExampleUnitTest.java ================================================ package com.ken.tagimage; import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void addition_isCorrect() throws Exception { assertEquals(4, 2 + 2); } } ================================================ FILE: build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() jcenter() } } task clean(type: Delete) { delete rootProject.buildDir } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Tue Aug 14 13:35:27 CST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip ================================================ FILE: gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: settings.gradle ================================================ include ':app'