[
  {
    "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/.idea/\n"
  },
  {
    "path": "README.md",
    "content": "# HencoderKeyboard3\n[HenCoder「仿写酷界面」活动——征稿](http://hencoder.com/activity-mock-1) <br>\n[HenCoder「仿写酷界面」活动——获奖作品点评](http://hencoder.com/activity-mock-2/)<br>\n虽然4个作品均未命中奖项，但本人还是会持续优化....，敬请关注！！！\n\n- 写作背景<br>\n在这个作品之前都是比较零散的搞一个比较好的效果仿写，比如我之前仿写 [QQ 健康效果](https://github.com/keyboard3/SelfView)。 hencoder 这个系列很完整，比之其他博主写的形式更加通俗易懂，我觉得这些都是次要的，都是知识获取渠道，最重要的是将知识落地。这场比赛我觉得很好，投稿、点评很能够调用一个开发者的兴趣。说实话在邀请投稿文章出来之后，自定义 View 经验不是很丰富的我居然在十几个小时就连续将4个作品全部仿写出来投稿了，可以说是调动了我很高的兴趣和潜力。当然作品还是有很多欠缺的地方，我会不断的进行完善。\n## 下载\n[demo.apk](app/build/outputs/apk/debug/app-debug.apk)\n\n# 即刻点赞\n- 写在前面\n获奖作品[ThumbUpSample](https://github.com/arvinljw/ThumbUpSample)<br>\n```\n    在揭晓优胜者之后我对比了实现原理，基本原理就是左边炫丽的效果和右边文字上下滚动分离。我在左边的效果实现上忘记了一个光圈的缩放，当时以为是鼠标自己的效果....，比较遗憾！\n```\n- 实现原理\n```\n实现原理：隔离并复用图片动画和文字动画\nLikeView自定义的LinearLayout默认组合点赞文字动画效果\nLikeImageView\n点赞图片动画\n点赞效果\n    灰色的点赞图标变小至0.9倍\n    转变成红色的图标并且半透明\n    红色图标逐渐增长至正常的1.1倍 / 透明度在增长至正常时逐渐变成实体 最后变成正常大小的红色图标\n点赞伴随动画光圈\n    0.6倍点赞图标大小的光圈、半透明\n    0-50%动画完成度时半透明变成实体\n    50%-100%动画完成度时实体又逐渐变成透明\n    光圈半径逐渐增大至1.1倍\n取消点赞效果\n    红色图标变小至0.9倍且变成半透明 动画完成到一半时变成灰色的正常大小\n闪光动画\n    点赞时 闪光图标在点赞图标顶部的某个位置，先由小到大直至正常大小\n\nLikeNumView\n    点赞和取消赞动作导致的文字变化 转变成 原数字->新数字。点赞和取消赞时改动新数字的值(+1/-1)。将两个数字动转为字符串数组，从高位开始循环 如果数字相同就直接画数字，如果数字不同就开始绘制两个数字位移同时设置对应的透明渐变`\n```\n- 对比获奖作品\n```\n获奖作品之后并没有多大改动\n    点赞散开点并没有做处理\n我主要是在我原来的基础上将基本特效都实现了，包括点赞散开的效果\n```\n- 截图\n<img src=\"images/like.gif\" width=\"350\"><br>\n\n# 薄荷健康尺\n\n- 写在前面\n获奖作品[BooheeRuler](https://github.com/totond/BooheeRuler)<br>\n```\n在揭晓获奖作品之后,看了当时的实现方式原理一致，中央高亮刻度覆盖尺子之上，尺子刻度值全部绘制出来，然后滑动内容。因为当时滑动交互算是hencoder的超纲内容，因而漏写了这一块很遗憾！\n```\n- 实现原理\n```\n分析：\n    此控件分两块，下方尺子和上方显示的中央刻度值。下方尺子中刻度内容可以滑动而在其上的中央高亮刻度固定。由上分析：将此控件分成两个view，rulerView和rulerNumerView\n    rulerView:刻度要可以滑动即刻度是一个独立的View,上方固定的中央刻度则是ViewGroup在刻度子View绘制完成之后在上面覆盖绘制一个高亮的刻度线\n    rulerNumberView:实时监听rulerView的刻度滚动经过中央高亮刻度线时的值显示\n```\n- 对比获奖作品\n```\n获奖作品在众星捧月中不断进步,截止0.1.3版本\n    功能上：实现了尺子的左上右下的四种显示方向\n    性能上：每次重绘只绘制当前显示部分刻度\n我觉得它已经做的很完美了，但是我觉得还是有些补足之处的。\n    1.实现四种显示方向结构太臃肿、完全可以直接在InnerRuler中绘制刻度和文字等处做方向处理调整绘制坐标\n    2.监听刻度值的View可以做的更加解耦一些，rulerView和rulerNumberView可以做到n:n\n我这里在他的作品之上做了上面我所说的改造，因为时间关系我阉割了他边界阴影效果\n```\n- 截图\n<img src=\"images/ruler.gif\" width=\"350\">\n\n## 小米运动\n获奖作品[MISportsConnectWidget](https://github.com/sickworm/MISportsConnectWidget)\n\n- 写在前面\n```\n这个作品难点在于喷射的效果，因为经验较少直接采用了笨办法，稍微处理了一下随机算法。\n最后看到有人实现这个效果之后真心佩服，原作者点评也很专业\n```\n- 实现原理\n```\n带粒子喷射的头部以及虚化线尾巴旋转效果\n最终页面显示结果的外光晕旋转\n```\n- 作品对比\n```\n相形见绌啊，只讲我的缺点吧\n粒子喷射效果没有已随机点击代替...\n最终结果的外圈旋转光晕没有...\n```\n- 截图\n<img src=\"images/miMove.gif\" width=\"350\">\n\n## Fliboard 翻页效果\n获奖作品[HenCoderPractice](https://github.com/sunnyxibei/HenCoderPractice)\n- 写在前面\n```\n这个是4个中我最先写出来的，稿子发出来之后，下班之前就发了出来。起初有点懵，当效果分拆成小块小块的时候，发现很容易实现，最后拼起来效果一致。\n当这个作品没获奖的时候真的比较遗憾，可能是输给细节吧！\n```\n- 实现原理\n```\n将效果进行拆分成启动右侧的翻折、旋转翻折、结尾下方也翻折\n难点在于如果实现旋转翻折，翻折部分和正常部分分开绘制。\n带着翻折部分旋转：裁剪出翻折部分的canvas，将这部分canvs依照整体做旋转，然后将图片绘制在上面，最后反向旋转回来然后将这部分与正常部分拼接就完成了整个效果\n```\n- 作品对比\n```\n差异不大，获奖者比较鸡贼，图片采用他们最新的图标....\n```\n\n- 截图\n<img src=\"images/flipboard.gif\" width=\"350\">\n\n## 关于我\n\n简书 [keyboard3](http://www.jianshu.com/users/62329de8c8a6/latest_articles)<br>\n邮箱 keyboard3@icloud.com\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 25\n    buildToolsVersion '26.0.2'\n    defaultConfig {\n        applicationId \"com.keyboard3.hencoderProduct\"\n        minSdkVersion 18\n        targetSdkVersion 25\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\napply plugin: 'replugin-plugin-gradle'\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation 'com.qihoo360.replugin:replugin-plugin-lib:2.2.0'\n    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {\n        exclude group: 'com.android.support', module: 'support-annotations'\n    })\n    compile 'com.android.support:appcompat-v7:25.3.1'\n    compile 'com.android.support.constraint:constraint-layout:1.0.2'\n    compile 'com.android.support:support-core-ui:25.3.1'\n    compile 'com.android.support:design:25.3.1'\n    testCompile 'junit:junit:4.12'\n    compile project(':ruler')\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/rengwuxian/.android-sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\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/keyboard3/hencoderProduct/ExampleInstrumentedTest.java",
    "content": "package com.keyboard3.hencoderProduct;\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 * Instrumentation 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.hencoder.hencoderpracticedraw5\", 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.keyboard3.hencoderProduct\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\"com.keyboard3.hencoderProduct.MainActivity\">\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/keyboard3/hencoderProduct/MainActivity.java",
    "content": "package com.keyboard3.hencoderProduct;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.MessageQueue;\nimport android.support.annotation.LayoutRes;\nimport android.support.annotation.StringRes;\nimport android.support.design.widget.TabLayout;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentPagerAdapter;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.Menu;\n\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MainActivity extends AppCompatActivity {\n    TabLayout tabLayout;\n    ViewPager pager;\n    List<PageModel> pageModels = new ArrayList<>();\n\n    {\n        pageModels.add(new PageModel(R.layout.like_layout, R.string.title_like));\n        pageModels.add(new PageModel(R.layout.ruler_layout, R.string.title_ruler));\n        pageModels.add(new PageModel(R.layout.move_layout, R.string.title_move));\n        pageModels.add(new PageModel(R.layout.filpboard_layout, R.string.title_flipboard));\n    }\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        pager = (ViewPager) findViewById(R.id.pager);\n        pager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {\n\n            @Override\n            public Fragment getItem(int position) {\n                PageModel pageModel = pageModels.get(position);\n                return PageFragment.newInstance(pageModel.productLayoutRes);\n            }\n\n            @Override\n            public int getCount() {\n                return pageModels.size();\n            }\n\n            @Override\n            public CharSequence getPageTitle(int position) {\n                return getString(pageModels.get(position).titleRes);\n            }\n        });\n\n        tabLayout = (TabLayout) findViewById(R.id.tabLayout);\n        tabLayout.setupWithViewPager(pager);\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        return super.onCreateOptionsMenu(menu);\n    }\n\n    private class PageModel {\n        @LayoutRes\n        int productLayoutRes;\n        @StringRes\n        int titleRes;\n\n        PageModel(@LayoutRes int sampleLayoutRes, @StringRes int titleRes) {\n            this.productLayoutRes = sampleLayoutRes;\n            this.titleRes = titleRes;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/keyboard3/hencoderProduct/PageFragment.java",
    "content": "package com.keyboard3.hencoderProduct;\n\nimport android.os.Bundle;\nimport android.support.annotation.LayoutRes;\nimport android.support.annotation.Nullable;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewStub;\n\n\npublic class PageFragment extends Fragment {\n    @LayoutRes\n    int productLayoutRes;\n\n    public static PageFragment newInstance(@LayoutRes int productLayoutRes) {\n        PageFragment fragment = new PageFragment();\n        Bundle args = new Bundle();\n        args.putInt(\"productLayoutRes\", productLayoutRes);\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        View view = inflater.inflate(R.layout.fragment_page, container, false);\n\n        ViewStub sampleStub = (ViewStub) view.findViewById(R.id.sampleStub);\n        sampleStub.setLayoutResource(productLayoutRes);\n        sampleStub.inflate();\n\n        return view;\n    }\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Bundle args = getArguments();\n        if (args != null) {\n            productLayoutRes = args.getInt(\"productLayoutRes\");\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/keyboard3/hencoderProduct/Utils.java",
    "content": "package com.keyboard3.hencoderProduct;\n\nimport android.content.res.Resources;\nimport android.util.DisplayMetrics;\n\npublic class Utils {\n    public static float dpToPixel(float dp) {\n        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();\n        return dp * metrics.density;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/keyboard3/hencoderProduct/filpboard/FlipboardLayout.java",
    "content": "package com.keyboard3.hencoderProduct.filpboard;\n\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.view.animation.LinearInterpolator;\nimport android.widget.Button;\nimport android.widget.RelativeLayout;\n\nimport com.keyboard3.hencoderProduct.R;\n\n\npublic class FlipboardLayout extends RelativeLayout {\n    FlipboardView view;\n    Button animateBt;\n\n    public FlipboardLayout(Context context) {\n        super(context);\n    }\n\n    public FlipboardLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public FlipboardLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    public void draw(Canvas canvas) {\n        super.draw(canvas);\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n\n        view = (FlipboardView) findViewById(R.id.objectAnimatorView);\n        animateBt = (Button) findViewById(R.id.animateBt);\n\n        animateBt.setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                view.clearCanvas();\n\n                ObjectAnimator animator1 = ObjectAnimator.ofInt(view, \"turnoverDegreeFirst\", 0, 30);\n                animator1.setDuration(1000);\n                animator1.setInterpolator(new LinearInterpolator());\n\n                ObjectAnimator animator2 = ObjectAnimator.ofInt(view, \"degree\", 0, 270);\n                animator2.setDuration(2000);\n                animator2.setInterpolator(new AccelerateDecelerateInterpolator());\n\n                ObjectAnimator animator3 = ObjectAnimator.ofInt(view, \"turnoverDegreeLast\", 0, 30);\n                animator3.setDuration(1000);\n                animator3.setInterpolator(new LinearInterpolator());\n\n                AnimatorSet animatorSet = new AnimatorSet();\n                // 两个动画依次执行\n                animatorSet.playSequentially(animator1, animator2, animator3);\n                animatorSet.start();\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/keyboard3/hencoderProduct/filpboard/FlipboardView.java",
    "content": "package com.keyboard3.hencoderProduct.filpboard;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.Camera;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.support.annotation.Nullable;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport com.keyboard3.hencoderProduct.R;\n\n\n/**\n * @author keyboard3\n */\npublic class FlipboardView extends View {\n    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n    private Camera mCamera = new Camera();\n    private Bitmap mBitmap;\n    private int turnoverDegreeFirst;\n    private int turnoverDegreeLast;\n    private int degree;\n\n    public FlipboardView(Context context) {\n        super(context);\n    }\n\n    public FlipboardView(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public FlipboardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    {\n        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.filpboard);\n    }\n\n    public void clearCanvas() {\n        degree = 0;\n        turnoverDegreeFirst = 0;\n        turnoverDegreeLast = 0;\n        invalidate();\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n\n        int bitmapWidth = mBitmap.getWidth();\n        int bitmapHeight = mBitmap.getHeight();\n        int centerX = getWidth() / 2;\n        int centerY = getHeight() / 2;\n        int x = centerX - bitmapWidth / 2;\n        int y = centerY - bitmapHeight / 2;\n\n\n        //绘制上半部分\n        canvas.save();\n        canvas.rotate(-degree, centerX, centerY);\n        canvas.clipRect(0, 0, centerX, getHeight());\n        canvas.rotate(degree, centerX, centerY);\n\n        //翻折画布\n        mCamera.save();\n        mCamera.rotateX(-turnoverDegreeLast);\n        canvas.translate(centerX, centerY);\n        mCamera.applyToCanvas(canvas);\n        canvas.translate(-centerX, -centerY);\n        mCamera.restore();\n\n        canvas.drawBitmap(mBitmap, x, y, mPaint);\n        canvas.restore();\n\n        //绘制右边部分\n        canvas.save();\n        canvas.rotate(-degree, centerX, centerY);\n        canvas.clipRect(centerX, 0, getWidth(), getHeight());\n\n        //翻折画布\n        mCamera.save();\n        mCamera.rotateY(-turnoverDegreeFirst);\n        canvas.translate(centerX, centerY);\n        mCamera.applyToCanvas(canvas);\n        canvas.translate(-centerX, -centerY);\n        mCamera.restore();\n        canvas.rotate(degree, centerX, centerY);\n\n        //向画布绘制内容\n        canvas.drawBitmap(mBitmap, x, y, mPaint);\n        canvas.restore();\n\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setDegree(int degree) {\n        this.degree = degree;\n        invalidate();\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setTurnoverDegreeLast(int turnoverDegreeLast) {\n        this.turnoverDegreeLast = turnoverDegreeLast;\n        invalidate();\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setTurnoverDegreeFirst(int turnoverDegreeFirst) {\n        this.turnoverDegreeFirst = turnoverDegreeFirst;\n        invalidate();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/keyboard3/hencoderProduct/like/LikeImageView.java",
    "content": "package com.keyboard3.hencoderProduct.like;\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.support.annotation.IdRes;\nimport android.support.annotation.Nullable;\nimport android.util.AttributeSet;\nimport android.view.View;\n\n/**\n * @author keyboard3\n * @date 2017/10/30\n */\n\npublic class LikeImageView extends View {\n    private Paint mImagePaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n    private boolean liked = false;\n    private float animProgress;\n    private Bitmap mUnlikeBitmap;\n    private Bitmap mLikedBitmap;\n    private Bitmap mShiningBitmap;\n    private float leftPadding;\n    private float middlePadding;\n    private float startX;\n    private int centerY;\n\n    public LikeImageView(Context context) {\n        super(context);\n    }\n\n    public LikeImageView(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public LikeImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    {\n        mImagePaint.setColor(Color.parseColor(\"#c3c4c3\"));\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        startX = mLikedBitmap.getWidth() * 0.1f + leftPadding;\n        int width = (int) (mLikedBitmap.getWidth() * 1.1);\n        int height = mLikedBitmap.getHeight() + mShiningBitmap.getHeight();\n        setMeasuredDimension((int) (width + leftPadding + middlePadding), height);\n    }\n\n    public void setLike(boolean isLike) {\n        liked = isLike;\n        if (!liked) {\n            animProgress = 0;\n        }\n        invalidate();\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        centerY = getHeight() / 2;\n        //绘制放大圈\n        drawCircle(canvas, (int) (startX + mLikedBitmap.getWidth() / 2), centerY);\n        //绘制点赞图片\n        int likeTop = centerY - mUnlikeBitmap.getHeight() / 2;\n        drawLike(canvas, likeTop, (int) startX);\n        //绘制闪光\n        drawShining(canvas, likeTop, (int) startX);\n    }\n\n    private void drawCircle(Canvas canvas, int centerX, int centerY) {\n        float radius = 0;\n        int alpha = 0;\n        if (liked&&animProgress > 0) {\n            //透明变实体\n            if (animProgress > 0 && animProgress <= 0.5) {\n                alpha = (int) (255 * (0.5 + animProgress));\n            } else { //实体变透明\n                alpha = (int) (255 * (1 - (animProgress - 0.5) * 2));\n            }\n            radius = (float) (0.6 + animProgress * 0.7);\n        }\n        mImagePaint.setColor(Color.parseColor(\"#cc775c\"));\n        mImagePaint.setAlpha(alpha);\n        mImagePaint.setStyle(Paint.Style.STROKE);\n        mImagePaint.setStrokeWidth(3);\n        canvas.drawCircle(centerX, centerY, radius * mLikedBitmap.getWidth() / 2, mImagePaint);\n        mImagePaint.setColor(Color.parseColor(\"#c3c4c3\"));\n        mImagePaint.setStyle(Paint.Style.FILL);\n    }\n\n    private void drawLike(Canvas canvas, int likeTop, int likeLeft) {\n        Bitmap bitmap = null;\n        float scale = 0;\n        if (liked) {\n            //灰色一下变小\n            if (animProgress > 0 && animProgress <= 0.1) {\n                bitmap = mUnlikeBitmap;\n                scale = -0.01f;\n            }\n            //红色小且半透明 变正常过程就变成了实体\n            if (animProgress > 0.1 && animProgress <= 0.5) {\n                mImagePaint.setAlpha((int) (255 * (0.5 + animProgress)));\n            } else {\n                mImagePaint.setAlpha(255);\n            }\n            //红色放大\n            if (animProgress > 0.1 && animProgress <= 0.9) {\n                bitmap = mLikedBitmap;\n                scale = (float) (-0.01f + animProgress * 0.1);\n            }\n            //一瞬间变正常\n            if (animProgress > 0.9 || animProgress == 0) {\n                bitmap = mLikedBitmap;\n                scale = 0;\n            }\n        } else {\n            //红色缩小 变半透明\n            if (animProgress > 0 && animProgress <= 0.5) {\n                bitmap = mLikedBitmap;\n                mImagePaint.setAlpha((int) (255 * (0.5 + animProgress)));\n                scale = (float) (-animProgress * 0.1);\n            }\n            //一半的时候变灰色\n            if (animProgress > 0.5 || animProgress == 0) {\n                mImagePaint.setAlpha(255);\n                bitmap = mUnlikeBitmap;\n                scale = 0;\n            }\n        }\n        canvas.save();\n        canvas.translate((float) (likeLeft - bitmap.getWidth() * 0.05), (float) (likeTop - bitmap.getHeight() * 0.05));\n        canvas.scale(1 + scale, 1 + scale);\n        canvas.drawBitmap(bitmap, 0, 0, mImagePaint);\n        canvas.restore();\n    }\n\n    private void drawShining(Canvas canvas, int likeTop, int likeLeft) {\n        if (liked&&animProgress > 0) {\n            float scale = animProgress;\n            int shiningTop = (int) (likeTop - scale * mShiningBitmap.getHeight() / 2);\n            int shiningLeft = (int) (likeLeft + (mLikedBitmap.getWidth() - scale * mShiningBitmap.getWidth()) / 2);\n\n            canvas.save();\n            canvas.translate(shiningLeft, shiningTop);\n            canvas.scale(scale, scale);\n            canvas.drawBitmap(mShiningBitmap, 0, 0, mImagePaint);\n            canvas.restore();\n        }\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setAnimProgress(float animProgress) {\n        this.animProgress = animProgress;\n        invalidate();\n    }\n\n    public void setMiddlePadding(float middlePadding) {\n        this.middlePadding = middlePadding;\n    }\n\n    public void setLeftPadding(float leftPadding) {\n        this.leftPadding = leftPadding;\n    }\n\n    public void setUnlikeSrc(@IdRes int unlikeSrc) {\n        mUnlikeBitmap = BitmapFactory.decodeResource(getResources(), unlikeSrc);\n    }\n\n    public void setLikedSrc(@IdRes int likeSrc) {\n        mLikedBitmap = BitmapFactory.decodeResource(getResources(), likeSrc);\n    }\n\n    public void setShiningdSrc(@IdRes int shiningSrc) {\n        mShiningBitmap = BitmapFactory.decodeResource(getResources(), shiningSrc);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/keyboard3/hencoderProduct/like/LikeLayout.java",
    "content": "package com.keyboard3.hencoderProduct.like;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.RelativeLayout;\n\nimport com.keyboard3.hencoderProduct.R;\n\n\npublic class LikeLayout extends RelativeLayout {\n    LikeView view1, view2, view3;\n    Button animateBt;\n\n    public LikeLayout(Context context) {\n        super(context);\n    }\n\n    public LikeLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public LikeLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n\n        view1 = (LikeView) findViewById(R.id.objectAnimatorView1);\n        view2 = (LikeView) findViewById(R.id.objectAnimatorView2);\n        view3 = (LikeView) findViewById(R.id.objectAnimatorView3);\n        animateBt = (Button) findViewById(R.id.animateBt);\n\n        animateBt.setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                view1.performClick();\n                view2.performClick();\n                view3.performClick();\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/keyboard3/hencoderProduct/like/LikeNumView.java",
    "content": "package com.keyboard3.hencoderProduct.like;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.support.annotation.Nullable;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport com.keyboard3.hencoderProduct.Utils;\n\n\n/**\n * @author keyboard3\n */\npublic class LikeNumView extends View {\n    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n    private int mCurNum = 0;\n    private int mNewNum = 0;\n    private int translationY;\n    private boolean liked = false;\n    private int mMoveY;\n    private int mTextSize;\n    private int centerY;\n    private float rightPadding;\n\n    public LikeNumView(Context context) {\n        super(context);\n    }\n\n    public LikeNumView(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public LikeNumView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    {\n        mTextSize = (int) Utils.dpToPixel(12);\n        mPaint.setTextSize(mTextSize);\n        mPaint.setColor(Color.parseColor(\"#c3c4c3\"));\n    }\n\n    protected void setNum(int num) {\n        mCurNum = mNewNum = num;\n        invalidate();\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        //保证长度足够\n        String curNum = (mCurNum + 1) * 10 + \"\";\n        Rect rect = new Rect();\n        mPaint.getTextBounds(curNum, 0, curNum.length(), rect);\n        float width = rect.width() + rightPadding;\n        int height = mTextSize * 3;\n        int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);\n        width = resolveSize((int) width, widthSpecSize);\n        setMeasuredDimension((int) width, height);\n        mMoveY = height / 2;\n    }\n\n    protected void changeLike(boolean isLike) {\n        if (isLike) {\n            if (mCurNum != 0) {\n                mNewNum = mCurNum - 1;\n            }\n        } else {\n            mNewNum = mCurNum + 1;\n        }\n        liked = !isLike;\n    }\n\n    protected void init() {\n        mCurNum = mNewNum;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        centerY = getHeight() / 2;\n        int leftX = 0;\n        Rect rect = new Rect();\n        mPaint.getTextBounds(\"0\", 0, 1, rect);\n        drawAnimNum(canvas, leftX, centerY - (rect.top + rect.bottom) / 2, mCurNum, mNewNum);\n    }\n\n    private void drawAnimNum(Canvas canvas, int leftX, int baseTxtY, int curNum, int newNum) {\n        String curNumStr = (curNum + \"\").toString();\n        String newNumStr = (newNum + \"\").toString();\n        int len = Math.max(curNumStr.length(), newNumStr.length());\n        float charLen = mPaint.measureText(\"0\");\n        int sumLeft = leftX;\n        String curCharTxt, newCharTxt;\n        for (int i = 0; i < len; i++) {\n            if (i > curNumStr.length() - 1) {\n                curCharTxt = \"\";\n            } else {\n                curCharTxt = curNumStr.substring(i, i + 1);\n            }\n            if (i > newNumStr.length() - 1) {\n                newCharTxt = \"\";\n            } else {\n                newCharTxt = newNumStr.substring(i, i + 1);\n            }\n            optDrawNum(canvas, sumLeft, baseTxtY, curCharTxt, newCharTxt, newNum > curNum);\n            sumLeft += charLen;\n        }\n    }\n\n    private void optDrawNum(Canvas canvas, int leftX, int baseTxtY, String curNum, String newNum, boolean upOrDown) {\n        if (curNum.equals(newNum)) {\n            mPaint.setAlpha(255);\n            canvas.drawText(curNum, leftX, baseTxtY, mPaint);\n            return;\n        }\n        int alpha = (int) ((1 - 1.0 * translationY / mMoveY) * 255);\n        int curBaseY = baseTxtY;\n        int newBaseY, transY;\n        if (upOrDown) {\n            transY = -translationY;\n            newBaseY = baseTxtY + mMoveY;\n        } else {//down -1\n            transY = translationY;\n            newBaseY = baseTxtY - mMoveY;\n        }\n        mPaint.setAlpha(alpha);\n        canvas.drawText(curNum, leftX, curBaseY + transY, mPaint);\n        mPaint.setAlpha(255 - alpha);\n        canvas.drawText(newNum, leftX, newBaseY + transY, mPaint);\n        mPaint.setAlpha(255);\n    }\n\n    public void setLiked(boolean liked) {\n        this.liked = liked;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setTranslationY(int translationY) {\n        this.translationY = translationY;\n        invalidate();\n    }\n\n    public void setRightPadding(float rightPadding) {\n        this.rightPadding = rightPadding;\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/keyboard3/hencoderProduct/like/LikeView.java",
    "content": "package com.keyboard3.hencoderProduct.like;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.LinearLayout;\n\nimport com.keyboard3.hencoderProduct.R;\n\n/**\n * @author keyboard3\n * @date 2017/10/30\n */\n\npublic class LikeView extends LinearLayout {\n    private int mAnimTime = 500;\n    private LikeNumView likeNumView;\n    private LikeImageView likeImageView;\n    private AnimatorSet animatorSet;\n    private boolean isLike = false;\n\n    public LikeView(Context context) {\n        super(context);\n    }\n\n    public LikeView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public LikeView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        final TypedArray custom = context.obtainStyledAttributes(\n                attrs, R.styleable.LikeView, defStyleAttr, 0);\n        int likeNum = custom.getInt(R.styleable.LikeView_likeNum, 0);\n        boolean liked = custom.getBoolean(R.styleable.LikeView_liked, false);\n        float leftPadding = custom.getDimension(R.styleable.LikeView_leftPadding, 0);\n        float middlePadding = custom.getDimension(R.styleable.LikeView_middlePadding, 0);\n        float rightPadding = custom.getDimension(R.styleable.LikeView_rightPadding, 0);\n        int likeSrc = custom.getResourceId(R.styleable.LikeView_likeSrc, R.mipmap.ic_messages_like_selected);\n        int unlikeSrc = custom.getResourceId(R.styleable.LikeView_unlikeSrc, R.mipmap.ic_messages_like_unselected);\n        int shiningSrc = custom.getResourceId(R.styleable.LikeView_shiningSrc, R.mipmap.ic_messages_like_selected_shining);\n        likeImageView.setShiningdSrc(shiningSrc);\n        likeImageView.setLikedSrc(likeSrc);\n        likeImageView.setUnlikeSrc(unlikeSrc);\n        likeNumView.setRightPadding(rightPadding);\n        likeImageView.setLeftPadding(leftPadding);\n        likeImageView.setMiddlePadding(middlePadding);\n        likeNumView.setNum(likeNum);\n        setLike(liked);\n    }\n\n    {\n        setOrientation(HORIZONTAL);\n        likeImageView = new LikeImageView(getContext());\n        addView(likeImageView);\n        likeNumView = new LikeNumView(getContext());\n        addView(likeNumView);\n        setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                if (!animatorSet.isRunning()) {\n                    likeNumView.changeLike(isLike);\n                    isLike = !isLike;\n                    likeImageView.setLike(isLike);\n                    animatorSet.start();\n                }\n            }\n        });\n    }\n\n    public void setNum(int num) {\n        likeNumView.setNum(num);\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        animatorSet.end();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        int mMoveY = getMeasuredHeight() / 2;\n\n        ObjectAnimator numAnimator = ObjectAnimator.ofInt(likeNumView, \"translationY\", 0, mMoveY);\n        numAnimator.addListener(new AnimatorListenerAdapter() {\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                super.onAnimationEnd(animation);\n                likeNumView.init();\n            }\n        });\n        numAnimator.setDuration(mAnimTime);\n\n        ObjectAnimator imageAnimator = ObjectAnimator.ofFloat(likeImageView, \"animProgress\", 0, 1);\n        imageAnimator.setDuration(mAnimTime);\n\n        animatorSet = new AnimatorSet();\n        animatorSet.playTogether(numAnimator, imageAnimator);\n\n        setMeasuredDimension(likeImageView.getMeasuredWidth() + likeNumView.getMeasuredWidth(), getMeasuredHeight());\n    }\n\n    public void setLike(boolean like) {\n        isLike = like;\n        likeNumView.setLiked(isLike);\n        likeImageView.setLike(isLike);\n        invalidate();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/keyboard3/hencoderProduct/movement/MoveLayout.java",
    "content": "package com.keyboard3.hencoderProduct.movement;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.RelativeLayout;\nimport android.widget.Scroller;\n\nimport com.keyboard3.hencoderProduct.R;\n\n\npublic class MoveLayout extends RelativeLayout {\n    MoveView view;\n    Button animateBt;\n\n    public MoveLayout(Context context) {\n        super(context);\n    }\n\n    public MoveLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public MoveLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n\n        view = (MoveView) findViewById(R.id.objectAnimatorView);\n        animateBt = (Button) findViewById(R.id.animateBt);\n\n        OnClickListener listener = new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                view.startAnimal();\n            }\n        };\n        animateBt.setOnClickListener(listener);\n        view.setOnClickListener(listener);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/keyboard3/hencoderProduct/movement/MoveView.java",
    "content": "package com.keyboard3.hencoderProduct.movement;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.DashPathEffect;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.graphics.PathEffect;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.graphics.Shader;\nimport android.graphics.SweepGradient;\nimport android.support.annotation.Nullable;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport com.keyboard3.hencoderProduct.R;\nimport com.keyboard3.hencoderProduct.Utils;\n\nimport java.util.Random;\n\n\n/**\n * @author keyboard3\n */\npublic class MoveView extends View {\n    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n    private AnimatorSet mAnimatorSet = new AnimatorSet();\n    private boolean mDrawInner = false;\n    private boolean mDrawOut = false;\n    private boolean mDrawLoading = true;\n    private String mStepNum;\n    private String mKmNum;\n    private String mCalNum;\n    private Bitmap mWatchBitmap;\n    private Random mRandom;\n    private int transparentWhite;\n    private int degree = 0;\n    private int maxMove;\n    private int centerX;\n    private int centerY;\n\n\n    public MoveView(Context context) {\n        super(context);\n    }\n\n    public MoveView(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public MoveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    {\n        setWillNotDraw(false);\n        mPaint.setTextAlign(Paint.Align.CENTER);\n        transparentWhite = Color.parseColor(\"#00ffffff\");\n        maxMove = (int) Utils.dpToPixel(50);\n        mWatchBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_watch);\n        mRandom = new Random();\n        mStepNum = \"2274\";\n        mKmNum = \"1.5公里\";\n        mCalNum = \"34千卡\";\n    }\n\n    public void startAnimal() {\n        mDrawInner = false;\n        mDrawOut = false;\n        mDrawLoading = true;\n        AnimatorSet animatorSet = new AnimatorSet();\n        ObjectAnimator animator0 = ObjectAnimator.ofInt(MoveView.this, \"degree\", 0, 480);\n        animator0.setDuration(3000);\n        animator0.addListener(new AnimatorListener() {\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                mDrawLoading = false;\n            }\n        });\n        ObjectAnimator animator1 = ObjectAnimator.ofFloat(this, \"translationY\", 0, -maxMove);\n        animator1.setDuration(500);\n        animator1.addListener(new AnimatorListener() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n                mDrawOut = true;\n            }\n        });\n        ObjectAnimator animator2 = ObjectAnimator.ofFloat(this, \"translationY\", -maxMove, 0);\n        animator2.setDuration(500);\n        animator2.addListener(new AnimatorListener() {\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                mDrawInner = true;\n            }\n        });\n        animatorSet.playSequentially(animator0, animator1, animator2);\n        animatorSet.start();\n    }\n\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        mAnimatorSet.end();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() + 200);//延长内容\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        centerX = getWidth() / 2;\n        centerY = getHeight() / 2;\n        //绘制步数\n        mPaint.setStyle(Paint.Style.FILL);\n        mPaint.setTextSize(90);\n        mPaint.setColor(Color.parseColor(\"#ffffff\"));\n        Rect stepNumRect = new Rect();\n        mPaint.getTextBounds(mStepNum, 0, mStepNum.length() - 1, stepNumRect);\n        int stepNumBaseY = centerY - (stepNumRect.top + stepNumRect.bottom) / 2;\n        canvas.drawText(mStepNum, centerX, stepNumBaseY, mPaint);\n        //绘制km\n        mPaint.setTextSize(24);\n        mPaint.setColor(Color.parseColor(\"#b1d6f8\"));\n        Rect kmNumRect = new Rect();\n        mPaint.getTextBounds(mKmNum, 0, mKmNum.length() - 1, kmNumRect);\n        int kmNumBaseX = centerX - 20 - (kmNumRect.left + kmNumRect.right) / 2;\n        int kmNumBaseY = stepNumBaseY + 30 + kmNumRect.height();\n        canvas.drawText(mKmNum, kmNumBaseX, kmNumBaseY, mPaint);\n        //绘制卡路里\n        Rect calNumRect = new Rect();\n        mPaint.getTextBounds(mCalNum, 0, mCalNum.length() - 1, calNumRect);\n        int calNumBaseX = centerX + 20 + (calNumRect.left + calNumRect.right) / 2;\n        int calNumBaseY = stepNumBaseY + 30 + kmNumRect.height();\n        canvas.drawText(mCalNum, calNumBaseX, calNumBaseY, mPaint);\n        //绘制中间线\n        mPaint.setStrokeWidth(2);\n        int centerLineTop = kmNumBaseY - kmNumRect.height();\n        int centerLineBottom = centerLineTop + kmNumRect.height();\n        canvas.drawLine(centerX, centerLineTop, centerX, centerLineBottom, mPaint);\n        //绘制最底部手表\n        int watchX = centerX - mWatchBitmap.getWidth() / 2;\n        int watchY = centerLineBottom + 40;\n        canvas.drawBitmap(mWatchBitmap, watchX, watchY, mPaint);\n        //绘制刚开始的加载的旋转动画\n        if (mDrawLoading) {\n            canvas.save();\n            canvas.rotate(degree, centerX, centerY);\n            Shader mShader = new SweepGradient(centerX, centerY, transparentWhite, Color.WHITE);\n            mPaint.setStrokeWidth(1);\n            mPaint.setShader(mShader);\n            mPaint.setStyle(Paint.Style.STROKE);\n            int loadingRadius = (int) (0.65 * getMeasuredWidth() / 2);\n            RectF loadingCircle = new RectF(centerX - loadingRadius, centerY - loadingRadius, centerX + loadingRadius, centerY + loadingRadius);\n            Path loadingPath = new Path();\n            float loadingLeft = loadingCircle.left, loadingTop = loadingCircle.top, loadingRight = loadingCircle.right, loadingBottom = loadingCircle.bottom;\n            for (int i = 0; i < 20; i++) {\n                int value = mRandom.nextInt(25);\n                int sed = mRandom.nextInt(3);\n                loadingCircle.left = loadingLeft + value + sed;\n                loadingCircle.top = loadingTop + value - sed;\n                loadingCircle.right = loadingRight - value + sed;\n                loadingCircle.bottom = loadingBottom - value - sed;\n                loadingPath.addArc(loadingCircle, 40, 320);\n            }\n            canvas.drawPath(loadingPath, mPaint);\n\n            loadingPath.reset();\n            int decorPointX = centerX + loadingRadius;\n            int decorPointY = centerY + 5;\n            int tempX, tempY;\n            for (int i = 0; i < 10; i++) {\n                int value0 = mRandom.nextInt(i + 20);\n                int value = i * 2 + mRandom.nextInt(i + 20);\n                tempX = decorPointX - value0;\n                tempY = decorPointY - i * 2 - value;\n                loadingPath.addCircle(tempX, tempY, 5, Path.Direction.CCW);\n            }\n            mPaint.setStyle(Paint.Style.FILL);\n            canvas.drawPath(loadingPath, mPaint);\n            mPaint.setShader(null);\n            canvas.restore();\n        }\n        //绘制外轮廓\n        if (mDrawOut) {\n            int outRadius = (int) ((0.65 + 0.15 * getPercent()) * getMeasuredWidth() / 2);\n            mPaint.setStrokeWidth(25);\n            mPaint.setStyle(Paint.Style.STROKE);\n            canvas.drawCircle(centerX, centerY, outRadius, mPaint);\n        }\n        //绘制内轮廓\n        if (mDrawInner) {\n            mPaint.setStrokeWidth(5);\n            int innerRadius = (int) (0.55 * getMeasuredWidth() / 2);\n            int startRunAngle = -90, runedAngle = 270, startUnRunAngle = startRunAngle + runedAngle, unRunedAngle = 360 - runedAngle;\n            RectF innerCircle = new RectF(centerX - innerRadius, centerY - innerRadius, centerX + innerRadius, centerY + innerRadius);\n\n            PathEffect effects = new DashPathEffect(new float[]{2, 4}, 1);\n            Path unRunPath = new Path();\n            unRunPath.addArc(innerCircle, startUnRunAngle, unRunedAngle);\n            mPaint.setPathEffect(effects);\n            canvas.drawPath(unRunPath, mPaint);\n            mPaint.setPathEffect(null);\n\n            mPaint.setColor(Color.WHITE);\n            Path runedPath = new Path();\n            runedPath.addArc(innerCircle, startRunAngle, runedAngle);\n            canvas.drawPath(runedPath, mPaint);\n        }\n    }\n\n    private float getPercent() {\n        return Math.abs(getTranslationY()) / maxMove;\n    }\n\n    @Override\n    public void setTranslationY(float translationY) {\n        super.setTranslationY(translationY);\n        invalidate();\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setDegree(int degree) {\n        this.degree = degree;\n        invalidate();\n    }\n\n    public abstract class AnimatorListener implements Animator.AnimatorListener {\n\n        @Override\n        public void onAnimationStart(Animator animation) {\n\n        }\n\n        @Override\n        public void onAnimationCancel(Animator animation) {\n\n        }\n\n        @Override\n        public void onAnimationRepeat(Animator animation) {\n\n        }\n\n        @Override\n        public void onAnimationEnd(Animator animation) {\n\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/keyboard3/hencoderProduct/ruler/RulerLayout.java",
    "content": "package com.keyboard3.hencoderProduct.ruler;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.RelativeLayout;\n\nimport com.keyboard3.hencoderProduct.R;\n\n\npublic class RulerLayout extends RelativeLayout {\n    public RulerLayout(Context context) {\n        super(context);\n    }\n\n    public RulerLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public RulerLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout 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:orientation=\"vertical\"\n    android:background=\"#f8f8f8\">\n\n    <android.support.design.widget.TabLayout\n        android:id=\"@+id/tabLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:background=\"@android:color/white\"\n        app:tabTextAppearance=\"@android:style/TextAppearance.Widget.TabWidget\"\n        app:tabMode=\"scrollable\" />\n\n    <android.support.v4.view.ViewPager\n        android:id=\"@+id/pager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/filpboard_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.keyboard3.hencoderProduct.filpboard.FlipboardLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#2B2B2B\">\n\n    <com.keyboard3.hencoderProduct.filpboard.FlipboardView\n        android:id=\"@+id/objectAnimatorView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_centerInParent=\"true\" />\n\n    <Button\n        android:id=\"@+id/animateBt\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_margin=\"8dp\"\n        android:text=\"@string/animate\"\n        android:textSize=\"16sp\" />\n\n</com.keyboard3.hencoderProduct.filpboard.FlipboardLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_page.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <ViewStub\n        android:id=\"@+id/sampleStub\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:inflatedId=\"@+id/model\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/like_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.keyboard3.hencoderProduct.like.LikeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <com.keyboard3.hencoderProduct.like.LikeView\n            android:id=\"@+id/objectAnimatorView1\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:layout_margin=\"10dp\"\n            android:background=\"#2B2B2B\"\n            app:leftPadding=\"5dp\"\n            app:likeNum=\"9\"\n            app:likeSrc=\"@mipmap/ic_messages_like_selected\"\n            app:middlePadding=\"5dp\"\n            app:rightPadding=\"5dp\"\n            app:shiningSrc=\"@mipmap/ic_messages_like_selected_shining\"\n            app:unlikeSrc=\"@mipmap/ic_messages_like_unselected\" />\n\n        <com.keyboard3.hencoderProduct.like.LikeView\n            android:id=\"@+id/objectAnimatorView2\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:layout_margin=\"10dp\"\n            android:background=\"#2B2B2B\"\n            app:leftPadding=\"5dp\"\n            app:likeNum=\"239\"\n            app:likeSrc=\"@mipmap/ic_messages_like_selected\"\n            app:middlePadding=\"5dp\"\n            app:rightPadding=\"5dp\"\n            app:shiningSrc=\"@mipmap/ic_messages_like_selected_shining\"\n            app:unlikeSrc=\"@mipmap/ic_messages_like_unselected\" />\n\n        <com.keyboard3.hencoderProduct.like.LikeView\n            android:id=\"@+id/objectAnimatorView3\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:layout_margin=\"10dp\"\n            android:background=\"#2B2B2B\"\n            app:leftPadding=\"5dp\"\n            app:likeNum=\"100000\"\n            app:likeSrc=\"@mipmap/ic_messages_like_selected\"\n            app:liked=\"true\"\n            app:middlePadding=\"5dp\"\n            app:rightPadding=\"5dp\"\n            app:shiningSrc=\"@mipmap/ic_messages_like_selected_shining\"\n            app:unlikeSrc=\"@mipmap/ic_messages_like_unselected\" />\n    </LinearLayout>\n\n    <Button\n        android:id=\"@+id/animateBt\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_margin=\"8dp\"\n        android:text=\"anim\"\n        android:textSize=\"16sp\" />\n\n</com.keyboard3.hencoderProduct.like.LikeLayout>"
  },
  {
    "path": "app/src/main/res/layout/move_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.keyboard3.hencoderProduct.movement.MoveLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.keyboard3.hencoderProduct.movement.MoveView\n        android:id=\"@+id/objectAnimatorView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_centerInParent=\"true\"\n        android:background=\"@mipmap/bg_mi\"/>\n\n    <Button\n        android:id=\"@+id/animateBt\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"48dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_margin=\"8dp\"\n        android:text=\"@string/animate\"\n        android:textSize=\"16sp\" />\n\n</com.keyboard3.hencoderProduct.movement.MoveLayout>"
  },
  {
    "path": "app/src/main/res/layout/ruler_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.keyboard3.hencoderProduct.ruler.RulerLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:gravity=\"center\"\n        android:orientation=\"vertical\">\n\n        <com.keyboard3.RulerNumberLayout\n            android:id=\"@+id/knl_bottom_head\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:kgTextSize=\"20sp\"\n            app:scaleTextSize=\"50sp\" />\n\n        <com.keyboard3.BooheeRuler\n            android:id=\"@+id/br_top_head\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"100dp\"\n            android:layout_alignParentTop=\"true\"\n            app:bigScaleLength=\"40dp\"\n            app:bigScaleWidth=\"2.5dp\"\n            app:count=\"10\"\n            app:currentScale=\"666666\"\n            app:cursorDrawable=\"@drawable/cursor_shape\"\n            app:cursorHeight=\"45dp\"\n            app:cursorWidth=\"4dp\"\n            app:maxScale=\"2000000\"\n            app:minScale=\"464\"\n            app:numberTextSize=\"22sp\"\n            app:paddingStartAndEnd=\"10dp\"\n            app:rulerStyle=\"top\"\n            app:scaleInterval=\"11.5dp\"\n            app:smallScaleLength=\"20dp\"\n            app:smallScaleWidth=\"1.5dp\"\n            app:targetRulerNumber=\"@id/knl_bottom_head\"\n            app:textMarginHead=\"70dp\" />\n\n        <com.keyboard3.BooheeRuler\n            android:id=\"@+id/br_bottom_head\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"100dp\"\n            android:layout_alignParentTop=\"true\"\n            app:bigScaleLength=\"40dp\"\n            app:bigScaleWidth=\"2.5dp\"\n            app:count=\"10\"\n            app:currentScale=\"3456\"\n            app:cursorDrawable=\"@drawable/cursor_shape\"\n            app:cursorHeight=\"45dp\"\n            app:cursorWidth=\"4dp\"\n            app:maxScale=\"2000000\"\n            app:minScale=\"464\"\n            app:numberTextSize=\"22sp\"\n            app:paddingStartAndEnd=\"10dp\"\n            app:rulerStyle=\"bottom\"\n            app:scaleInterval=\"11.5dp\"\n            app:smallScaleLength=\"20dp\"\n            app:smallScaleWidth=\"1.5dp\"\n            app:targetRulerNumber=\"@id/knl_bottom_head\"\n            app:textMarginHead=\"70dp\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"horizontal\"\n            android:padding=\"10dp\">\n\n            <com.keyboard3.BooheeRuler\n                android:id=\"@+id/br_left_head\"\n                android:layout_width=\"100dp\"\n                android:layout_height=\"match_parent\"\n                android:layout_alignParentTop=\"true\"\n                app:bigScaleLength=\"40dp\"\n                app:bigScaleWidth=\"2.5dp\"\n                app:count=\"10\"\n                app:currentScale=\"23\"\n                app:cursorDrawable=\"@drawable/cursor_shape\"\n                app:cursorHeight=\"45dp\"\n                app:cursorWidth=\"4dp\"\n                app:maxScale=\"2000000\"\n                app:minScale=\"464\"\n                app:numberTextSize=\"22sp\"\n                app:paddingStartAndEnd=\"10dp\"\n                app:rulerStyle=\"left\"\n                app:scaleInterval=\"11.5dp\"\n                app:smallScaleLength=\"20dp\"\n                app:smallScaleWidth=\"1.5dp\"\n                app:targetRulerNumber=\"@id/knl_bottom_head\"\n                app:textMarginHead=\"70dp\" />\n\n            <com.keyboard3.RulerNumberLayout\n                android:id=\"@+id/knl_number\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                app:kgTextSize=\"20sp\"\n                app:kgUnitText=\"km\"\n                app:scaleTextSize=\"50sp\"\n                app:targetRuler=\"@id/br_bottom_head\" />\n\n            <com.keyboard3.BooheeRuler\n                android:id=\"@+id/br_right_head\"\n                android:layout_width=\"100dp\"\n                android:layout_height=\"match_parent\"\n                android:layout_alignParentTop=\"true\"\n                app:bigScaleLength=\"40dp\"\n                app:bigScaleWidth=\"2.5dp\"\n                app:count=\"10\"\n                app:currentScale=\"99\"\n                app:cursorDrawable=\"@drawable/cursor_shape\"\n                app:cursorHeight=\"45dp\"\n                app:cursorWidth=\"4dp\"\n                app:maxScale=\"2000000\"\n                app:minScale=\"464\"\n                app:numberTextSize=\"22sp\"\n                app:paddingStartAndEnd=\"10dp\"\n                app:rulerStyle=\"right\"\n                app:scaleInterval=\"11.5dp\"\n                app:smallScaleLength=\"20dp\"\n                app:smallScaleWidth=\"1.5dp\"\n                app:targetRulerNumber=\"@id/knl_bottom_head\"\n                app:textMarginHead=\"70dp\" />\n        </LinearLayout>\n    </LinearLayout>\n</com.keyboard3.hencoderProduct.ruler.RulerLayout>"
  },
  {
    "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\">HenCoder参赛作品by keyboard3</string>\n    <string name=\"title_flipboard\">Flipboard翻页</string>\n    <string name=\"title_like\">即刻点赞</string>\n    <string name=\"title_ruler\">薄荷健康尺</string>\n    <string name=\"animate\">animate</string>\n    <string name=\"title_move\">小米运动</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n    <declare-styleable name=\"LikeView\">\n        <attr name=\"likeNum\" format=\"integer\"></attr>\n        <attr name=\"leftPadding\" format=\"dimension\"></attr>\n        <attr name=\"rightPadding\" format=\"dimension\"></attr>\n        <attr name=\"middlePadding\" format=\"dimension\"></attr>\n        <attr name=\"liked\" format=\"boolean\"></attr>\n        <attr name=\"likeSrc\" format=\"reference\"></attr>\n        <attr name=\"unlikeSrc\" format=\"reference\"></attr>\n        <attr name=\"shiningSrc\" format=\"reference\"></attr>\n    </declare-styleable>\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\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    <style name=\"HenCoderTabTextAppearance\">\n        <item name=\"textAllCaps\">false</item>\n        <item name=\"android:textAllCaps\">false</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/test/java/com/keyboard3/hencoderProduct/ExampleUnitTest.java",
    "content": "package com.keyboard3.hencoderProduct;\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    repositories {\n        jcenter()\n        google()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.0.0'\n        classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.2.0'\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        jcenter()\n        google()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Fri Oct 13 16:17:40 CST 2017\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 [ -relativeX \"$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 [ ! -relativeX \"$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 \"relativeX%~1\" == \"relativeX\" 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": "ruler/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "ruler/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 25\n    buildToolsVersion '26.0.2'\n    defaultConfig {\n        minSdkVersion 16\n        targetSdkVersion 25\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation 'com.android.support:appcompat-v7:25.3.1'\n    testImplementation 'junit:junit:4.12'\n    androidTestImplementation 'com.android.support.test:runner:1.0.1'\n    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'\n}\n"
  },
  {
    "path": "ruler/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": "ruler/src/androidTest/java/com/keyboard3/ExampleInstrumentedTest.java",
    "content": "package com.keyboard3;\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.keyboard3.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "ruler/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.keyboard3\" />\n"
  },
  {
    "path": "ruler/src/main/java/com/keyboard3/BooheeRuler.java",
    "content": "package com.keyboard3;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.drawable.Drawable;\nimport android.support.annotation.ColorInt;\nimport android.support.annotation.IdRes;\nimport android.support.annotation.IntDef;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewTreeObserver;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * 用于包着尺子的外壳，用于画选取光标、外壳\n */\n\npublic class BooheeRuler extends ViewGroup {\n    private final String TAG = \"ruler\";\n    private Context mContext;\n    //尺子Style定义\n    public static final int TOP_LAYOUT = 1, BOTTOM_LAYOUT = 3, LEFT_LAYOUT = 0, RIGHT_LAYOUT = 2;\n\n    @IntDef({TOP_LAYOUT, BOTTOM_LAYOUT, LEFT_LAYOUT, RIGHT_LAYOUT})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface RulerStyle {\n    }\n\n    private @BooheeRuler.RulerStyle\n    int mStyle = TOP_LAYOUT;\n    //内部的尺子\n    private InnerRuler mInnerRuler;\n    //最小最大刻度值(以0.1kg为单位)\n    private int mMinScale = 464, mMaxScale = 2000;\n    //光标宽度、高度\n    private int mCursorWidth = 8, mCursorHeight = 70;\n    //大小刻度的长度\n    private int mSmallScaleLength = 30, mBigScaleLength = 60;\n    //大小刻度的粗细\n    private int mSmallScaleWidth = 3, mBigScaleWidth = 5;\n    //数字字体大小\n    private int mTextSize = 28;\n    //数字Text距离顶部高度\n    private int mTextMarginHead = 120;\n    //刻度间隔\n    private int mInterval = 18;\n    private @IdRes\n    int mTargetRulerNumer;\n    //数字Text颜色\n    private\n    @ColorInt\n    int mTextColor = getResources().getColor(R.color.colorLightBlack);\n    //刻度颜色\n    private\n    @ColorInt\n    int mScaleColor = getResources().getColor(R.color.colorGray);\n    //初始的当前刻度\n    private float mCurrentScale = 0;\n    /**\n     * 一格大刻度多少格小刻度\n     */\n    private int mCount = 10;\n    /**\n     * 光标drawable\n     */\n    private Drawable mCursorDrawable;\n    /**\n     * 尺子两端的padding\n     */\n    private int mPaddingHeadAndEnd = 0;\n    private int mPaddingLeft = 0, mPaddingTop = 0, mPaddingRight = 0, mPaddingBottom = 0;\n    /**\n     * 尺子背景\n     */\n    private Drawable mRulerBackGround;\n    private int mRulerBackGroundColor = getResources().getColor(R.color.colorDirtyWithe);\n    /**\n     * 是否启用边缘效应\n     */\n    private boolean mCanEdgeEffect = true;\n    /**\n     * 边缘颜色\n     */\n    private @ColorInt\n    int mEdgeColor = getResources().getColor(R.color.colorForgiven);\n\n\n    public BooheeRuler(Context context) {\n        super(context);\n        initRuler(context);\n\n    }\n\n    public BooheeRuler(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        initAttrs(context, attrs);\n        initRuler(context);\n\n    }\n\n    public BooheeRuler(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        initAttrs(context, attrs);\n        initRuler(context);\n\n    }\n\n    @SuppressWarnings(\"WrongConstant\")\n    private void initAttrs(Context context, AttributeSet attrs) {\n        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.BooheeRuler, 0, 0);\n        mMinScale = typedArray.getInteger(R.styleable.BooheeRuler_minScale, mMinScale);\n        mMaxScale = typedArray.getInteger(R.styleable.BooheeRuler_maxScale, mMaxScale);\n        mCursorWidth = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_cursorWidth, mCursorWidth);\n        mCursorHeight = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_cursorHeight, mCursorHeight);\n        mSmallScaleWidth = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_smallScaleWidth, mSmallScaleWidth);\n        mSmallScaleLength = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_smallScaleLength, mSmallScaleLength);\n        mBigScaleWidth = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_bigScaleWidth, mBigScaleWidth);\n        mBigScaleLength = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_bigScaleLength, mBigScaleLength);\n        mTextSize = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_numberTextSize, mTextSize);\n        mTextMarginHead = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_textMarginHead, mTextMarginHead);\n        mInterval = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_scaleInterval, mInterval);\n        mTextColor = typedArray.getColor(R.styleable.BooheeRuler_numberTextColor, mTextColor);\n        mScaleColor = typedArray.getColor(R.styleable.BooheeRuler_scaleColor, mScaleColor);\n        mCurrentScale = typedArray.getFloat(R.styleable.BooheeRuler_currentScale, (mMaxScale + mMinScale) / 2);\n        mCount = typedArray.getInt(R.styleable.BooheeRuler_count, mCount);\n        mCursorDrawable = typedArray.getDrawable(R.styleable.BooheeRuler_cursorDrawable);\n        mTargetRulerNumer = typedArray.getResourceId(R.styleable.BooheeRuler_targetRulerNumber, 0);\n        if (mCursorDrawable == null) {\n            mCursorDrawable = getResources().getDrawable(R.drawable.cursor_shape);\n        }\n        mPaddingHeadAndEnd = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_paddingStartAndEnd, mPaddingHeadAndEnd);\n        mStyle = typedArray.getInt(R.styleable.BooheeRuler_rulerStyle, mStyle);\n        mRulerBackGround = typedArray.getDrawable(R.styleable.BooheeRuler_rulerBackGround);\n        if (mRulerBackGround == null) {\n            mRulerBackGroundColor = typedArray.getColor(R.styleable.BooheeRuler_rulerBackGround, mRulerBackGroundColor);\n        }\n        mCanEdgeEffect = typedArray.getBoolean(R.styleable.BooheeRuler_canEdgeEffect, mCanEdgeEffect);\n        mEdgeColor = typedArray.getColor(R.styleable.BooheeRuler_edgeColor, mEdgeColor);\n        typedArray.recycle();\n    }\n\n    private void initRuler(Context context) {\n        mContext = context;\n        mInnerRuler = new InnerRuler(context, this, mStyle);\n        paddingHeadAndEnd();\n\n        //设置全屏，加入InnerRuler\n        LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);\n        mInnerRuler.setLayoutParams(layoutParams);\n        addView(mInnerRuler);\n        //设置ViewGroup可画\n        setWillNotDraw(false);\n\n        initDrawable();\n        initRulerBackground();\n    }\n\n    private void initRulerBackground() {\n        if (mRulerBackGround != null) {\n            mInnerRuler.setBackground(mRulerBackGround);\n        } else {\n            mInnerRuler.setBackgroundColor(mRulerBackGroundColor);\n        }\n    }\n\n    /**\n     * 在宽高初始化之后定义光标Drawable的边界\n     */\n    private void initDrawable() {\n        getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {\n            @Override\n            public boolean onPreDraw() {\n                getViewTreeObserver().removeOnPreDrawListener(this);\n                switch (mStyle) {\n                    case TOP_LAYOUT:\n                        mCursorDrawable.setBounds((getWidth() - mCursorWidth) / 2, 0\n                                , (getWidth() + mCursorWidth) / 2, mCursorHeight);\n                        break;\n                    case BOTTOM_LAYOUT:\n                        mCursorDrawable.setBounds((getWidth() - mCursorWidth) / 2, getHeight() - mCursorHeight\n                                , (getWidth() + mCursorWidth) / 2, getHeight());\n                        break;\n                    case LEFT_LAYOUT:\n                        mCursorDrawable.setBounds(0, (getHeight() - mCursorWidth) / 2\n                                , mCursorHeight, (getHeight() + mCursorWidth) / 2);\n                        break;\n                    case RIGHT_LAYOUT:\n                        mCursorDrawable.setBounds(getWidth() - mCursorHeight, (getHeight() - mCursorWidth) / 2\n                                , getWidth(), (getHeight() + mCursorWidth) / 2);\n                        break;\n                    default:\n                }\n\n                return false;\n            }\n        });\n\n    }\n\n    @Override\n    protected void dispatchDraw(Canvas canvas) {\n        super.dispatchDraw(canvas);\n        //画中间的选定光标，要在这里画，因为dispatchDraw()执行在onDraw()后面，这样子光标才能不被尺子的刻度遮蔽\n        mCursorDrawable.draw(canvas);\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n        if (changed) {\n            mInnerRuler.layout(mPaddingLeft, mPaddingTop, r - l - mPaddingRight, b - t - mPaddingBottom);\n        }\n    }\n\n    private void paddingHeadAndEnd() {\n        mPaddingBottom = mPaddingTop = InnerRuler.isVerticalRuler(mStyle) ? mPaddingHeadAndEnd : 0;\n        mPaddingRight = mPaddingLeft = InnerRuler.isVerticalRuler(mStyle) ? 0 : mPaddingHeadAndEnd;\n    }\n\n    /**\n     * 设置回调\n     *\n     * @param rulerCallback\n     */\n    public void addCallback(RulerCallback rulerCallback) {\n        mInnerRuler.addRulerCallback(rulerCallback);\n    }\n\n    /**\n     * 设置当前进度\n     *\n     * @param currentScale\n     */\n    public void setCurrentScale(float currentScale) {\n        mCurrentScale = currentScale;\n        mInnerRuler.setCurrentScale(currentScale);\n    }\n\n    /**\n     * 如果控件尺寸变化，中间光标的位置也要重新定义\n     *\n     * @param w\n     * @param h\n     * @param oldw\n     * @param oldh\n     */\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        initDrawable();\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        if (mTargetRulerNumer != 0) {\n            View view = getRootView().findViewById(mTargetRulerNumer);\n            if (view instanceof RulerCallback) {\n                RulerCallback callback = (RulerCallback) view;\n                addCallback(callback);\n            }\n        }\n    }\n\n    public int getEdgeColor() {\n        return mEdgeColor;\n    }\n\n    /**\n     * 设置能否使用边缘效果\n     *\n     * @param canEdgeEffect\n     */\n    public void setCanEdgeEffect(boolean canEdgeEffect) {\n        this.mCanEdgeEffect = canEdgeEffect;\n        mInnerRuler.initEdgeEffects();\n    }\n\n    public boolean canEdgeEffect() {\n        return mCanEdgeEffect;\n    }\n\n    public float getCurrentScale() {\n        return mCurrentScale;\n    }\n\n    public void setMinScale(int minScale) {\n        this.mMinScale = minScale;\n    }\n\n    public int getMinScale() {\n        return mMinScale;\n    }\n\n    public void setMaxScale(int maxScale) {\n        this.mMaxScale = maxScale;\n    }\n\n    public int getMaxScale() {\n        return mMaxScale;\n    }\n\n    public void setCursorWidth(int cursorWidth) {\n        this.mCursorWidth = cursorWidth;\n    }\n\n    public int getCursorWidth() {\n        return mCursorWidth;\n    }\n\n    public void setCursorHeight(int cursorHeight) {\n        this.mCursorHeight = cursorHeight;\n    }\n\n    public int getCursorHeight() {\n        return mCursorHeight;\n    }\n\n\n    public void setBigScaleLength(int bigScaleLength) {\n        this.mBigScaleLength = bigScaleLength;\n    }\n\n    public int getBigScaleLength() {\n        return mBigScaleLength;\n    }\n\n    public void setBigScaleWidth(int bigScaleWidth) {\n        this.mBigScaleWidth = bigScaleWidth;\n    }\n\n    public int getBigScaleWidth() {\n        return mBigScaleWidth;\n    }\n\n    public void setSmallScaleLength(int smallScaleLength) {\n        this.mSmallScaleLength = smallScaleLength;\n    }\n\n    public int getSmallScaleLength() {\n        return mSmallScaleLength;\n    }\n\n    public void setSmallScaleWidth(int smallScaleWidth) {\n        this.mSmallScaleWidth = smallScaleWidth;\n    }\n\n    public int getSmallScaleWidth() {\n        return mSmallScaleWidth;\n    }\n\n    public void setTextMarginTop(int textMarginTop) {\n        this.mTextMarginHead = textMarginTop;\n    }\n\n    public int getTextMarginHead() {\n        return mTextMarginHead;\n    }\n\n    public void setTextSize(int textSize) {\n        this.mTextSize = textSize;\n    }\n\n    public int getTextSize() {\n        return mTextSize;\n    }\n\n    public void setInterval(int interval) {\n        this.mInterval = interval;\n    }\n\n    public int getInterval() {\n        return mInterval;\n    }\n\n    public int getTextColor() {\n        return mTextColor;\n    }\n\n    public int getScaleColor() {\n        return mScaleColor;\n    }\n\n    public void setCount(int mCount) {\n        this.mCount = mCount;\n    }\n\n    public int getCount() {\n        return mCount;\n    }\n}\n"
  },
  {
    "path": "ruler/src/main/java/com/keyboard3/InnerRuler.java",
    "content": "package com.keyboard3;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.PointF;\nimport android.os.Build;\nimport android.support.annotation.Px;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewTreeObserver;\nimport android.widget.EdgeEffect;\nimport android.widget.OverScroller;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 内部尺子抽象类\n */\n\npublic class InnerRuler extends View {\n    protected Context mContext;\n    protected BooheeRuler mParent;\n\n    protected Paint mSmallScalePaint, mBigScalePaint, mTextPaint, mOutLinePaint;\n    /**\n     * 当前刻度值\n     */\n    protected float mCurrentScale = 0;\n    /**\n     * 最大刻度数\n     */\n    protected int mMaxLength = 0;\n    /**\n     * 长度、最小可滑动值、最大可滑动值\n     */\n    protected int mLength, mMinPosition = 0, mMaxPosition = 0;\n    /**\n     * 控制滑动\n     */\n    protected OverScroller mOverScroller;\n    /**\n     * 一格大刻度多少格小刻度\n     */\n    protected int mCount = 10;\n    /**\n     * 提前刻画量\n     */\n    protected int mDrawOffset;\n    /**\n     * 速度获取\n     */\n    protected VelocityTracker mVelocityTracker;\n    /**\n     * 惯性最大最小速度\n     */\n    protected int mMaximumVelocity, mMinimumVelocity;\n    /**\n     * 回调接口\n     */\n    protected List<RulerCallback> mRulerCallbacks = new ArrayList<>();\n    /**\n     * 边界效果\n     */\n    protected EdgeEffect mStartEdgeEffect, mEndEdgeEffect;\n    /**\n     * 边缘效应长度\n     */\n    protected int mEdgeLength;\n    int flag;\n    private float mLastY;\n    private float mLastX;\n\n    public InnerRuler(Context context, BooheeRuler booheeRuler, int flag) {\n        super(context);\n        mParent = booheeRuler;\n        this.flag = flag;\n        init(context);\n    }\n\n    private void init(Context context) {\n        mContext = context;\n\n        mMaxLength = mParent.getMaxScale() - mParent.getMinScale();\n        mCurrentScale = mParent.getCurrentScale();\n        mCount = mParent.getCount();\n        mDrawOffset = mCount * mParent.getInterval() / 2;\n\n        initPaints();\n\n        mOverScroller = new OverScroller(mContext);\n\n        //配置速度\n        mVelocityTracker = VelocityTracker.obtain();\n        mMaximumVelocity = ViewConfiguration.get(context)\n                .getScaledMaximumFlingVelocity();\n        mMinimumVelocity = ViewConfiguration.get(context)\n                .getScaledMinimumFlingVelocity();\n\n        initEdgeEffects();\n\n        //第一次进入，跳转到设定刻度\n        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {\n            @Override\n            public void onGlobalLayout() {\n                getViewTreeObserver().removeOnGlobalLayoutListener(this);\n                scroll2Scale(mCurrentScale);\n            }\n        });\n        checkAPILevel();\n    }\n\n    private void initPaints() {\n        mSmallScalePaint = new Paint();\n        mSmallScalePaint.setStrokeWidth(mParent.getSmallScaleWidth());\n        mSmallScalePaint.setColor(mParent.getScaleColor());\n        mSmallScalePaint.setStrokeCap(Paint.Cap.ROUND);\n\n        mBigScalePaint = new Paint();\n        mBigScalePaint.setColor(mParent.getScaleColor());\n        mBigScalePaint.setStrokeWidth(mParent.getBigScaleWidth());\n        mBigScalePaint.setStrokeCap(Paint.Cap.ROUND);\n\n        mTextPaint = new Paint();\n        mTextPaint.setAntiAlias(true);\n        mTextPaint.setColor(mParent.getTextColor());\n        mTextPaint.setTextSize(mParent.getTextSize());\n        mTextPaint.setTextAlign(Paint.Align.CENTER);\n        mOutLinePaint = new Paint();\n        mOutLinePaint.setStrokeWidth(0);\n        mOutLinePaint.setColor(mParent.getScaleColor());\n    }\n\n    /**\n     * 初始化边缘效果\n     */\n    public void initEdgeEffects() {\n        if (mParent.canEdgeEffect()) {\n            if (mStartEdgeEffect == null || mEndEdgeEffect == null) {\n                mStartEdgeEffect = new EdgeEffect(mContext);\n                mEndEdgeEffect = new EdgeEffect(mContext);\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n                    mStartEdgeEffect.setColor(mParent.getEdgeColor());\n                    mEndEdgeEffect.setColor(mParent.getEdgeColor());\n                }\n                mEdgeLength = mParent.getCursorHeight() + mParent.getInterval() * mParent.getCount();\n            }\n        }\n    }\n\n\n    private void checkAPILevel() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {\n            setLayerType(LAYER_TYPE_NONE, null);\n        }\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        this.mRulerCallbacks = null;\n    }\n\n    /**\n     * 设置尺子当前刻度\n     *\n     * @param currentScale\n     */\n    public void setCurrentScale(float currentScale) {\n        this.mCurrentScale = currentScale;\n        scroll2Scale(mCurrentScale);\n    }\n\n    public void addRulerCallback(RulerCallback RulerCallback) {\n        mRulerCallbacks.add(RulerCallback);\n    }\n\n    public float getCurrentScale() {\n        return mCurrentScale;\n    }\n\n    //======绘制逻辑=======\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        drawRulerBody(canvas, flag);\n    }\n\n    protected void drawRulerBody(Canvas canvas, int flag) {\n        float location = 0, length;\n        if (isVerticalRuler(flag)) {\n            //刻度是水平的 Y是固定的\n            location = getScrollY();\n            length = canvas.getHeight();\n        } else {\n            //刻度是竖直的 x是固定的\n            location = getScrollX();\n            length = canvas.getWidth();\n        }\n        int start = (int) ((location - mDrawOffset) / mParent.getInterval() + mParent.getMinScale());\n        int end = (int) ((location + length + mDrawOffset) / mParent.getInterval() + mParent.getMinScale());\n\n        for (int i = start; i <= end; i++) {\n            if (i >= mParent.getMinScale() && i <= mParent.getMaxScale()) {\n                if (i % mCount == 0) {\n                    PointF[] line = getLine(mParent, flag, mParent.getBigScaleLength(), i, canvas.getHeight(), canvas.getWidth());\n                    canvas.drawLine(line[0].x, line[0].y, line[1].x, line[1].y, mBigScalePaint);\n\n                    PointF textEndPoint = getTextPoint(mParent, flag, i, canvas.getHeight(), canvas.getWidth());\n                    canvas.drawText(String.valueOf(i / 10), textEndPoint.x, textEndPoint.y, mTextPaint);\n                } else {\n                    PointF[] line = getLine(mParent, flag, mParent.getSmallScaleLength(), i, canvas.getHeight(), canvas.getWidth());\n                    canvas.drawLine(line[0].x, line[0].y, line[1].x, line[1].y, mSmallScalePaint);\n                }\n            }\n        }\n        //画轮廓线\n        PointF[] outLine = getOutLine(this, flag, canvas.getHeight(), canvas.getWidth());\n        canvas.drawLine(outLine[0].x, outLine[0].y, outLine[1].x, outLine[1].y, mOutLinePaint);\n        PointF[] outLine2 = getOutLine(this, getNegativeFlag(flag), canvas.getHeight(), canvas.getWidth());\n        canvas.drawLine(outLine2[0].x, outLine2[0].y, outLine2[1].x, outLine2[1].y, mOutLinePaint);\n        PointF[] outLine3 = getOutLeftOrRightLine(this, flag, true, canvas.getHeight(), canvas.getWidth());\n        canvas.drawLine(outLine3[0].x, outLine3[0].y, outLine3[1].x, outLine3[1].y, mOutLinePaint);\n        PointF[] outLine4 = getOutLeftOrRightLine(this, flag, false, canvas.getHeight(), canvas.getWidth());\n        canvas.drawLine(outLine4[0].x, outLine4[0].y, outLine4[1].x, outLine4[1].y, mOutLinePaint);\n    }\n\n    protected static boolean isVerticalRuler(int flag) {\n        return flag == 0 || flag == 2;\n    }\n\n    protected static int getNegativeFlag(int flag) {\n        return (flag + 2) % 4;\n    }\n\n    private static PointF getTextPoint(BooheeRuler mParent, int flag, float location, int height, int width) {\n        PointF pointF = new PointF();\n        float locationY = 0, locationX = 0;\n        if (isVerticalRuler(flag)) {\n            //刻度是水平的 Y是固定的\n            locationY = (location - mParent.getMinScale()) * mParent.getInterval();\n        } else {\n            //刻度是竖直的 x是固定的\n            locationX = (location - mParent.getMinScale()) * mParent.getInterval();\n        }\n        switch (flag) {\n            case BooheeRuler.LEFT_LAYOUT:\n                pointF.x = mParent.getTextMarginHead();\n                pointF.y = locationY + mParent.getTextSize() / 2;\n                break;\n            case BooheeRuler.TOP_LAYOUT:\n                pointF.x = locationX;\n                pointF.y = mParent.getTextMarginHead();\n                break;\n            case BooheeRuler.RIGHT_LAYOUT:\n                pointF.x = width - mParent.getTextMarginHead();\n                pointF.y = locationY + mParent.getTextSize() / 2;\n                break;\n            case BooheeRuler.BOTTOM_LAYOUT:\n                pointF.x = locationX;\n                pointF.y = height - mParent.getTextMarginHead();\n                break;\n            default:\n        }\n        return pointF;\n    }\n\n\n    private static PointF[] getLine(BooheeRuler mParent, int flag, int length, float location, int height, int width) {\n        PointF startPoint = new PointF();\n        PointF stopPoint = new PointF();\n        float locationY = 0, locationX = 0;\n        if (isVerticalRuler(flag)) {\n            //刻度是水平的 Y是固定的\n            locationY = (location - mParent.getMinScale()) * mParent.getInterval();\n        } else {//水平的尺子\n            //刻度是竖直的 x是固定的\n            locationX = (location - mParent.getMinScale()) * mParent.getInterval();\n        }\n        switch (flag) {\n            case BooheeRuler.LEFT_LAYOUT:\n                startPoint.y = locationY;\n                stopPoint.x = length;\n                stopPoint.y = locationY;\n                break;\n            case BooheeRuler.TOP_LAYOUT:\n                startPoint.x = locationX;\n                stopPoint.x = locationX;\n                stopPoint.y = length;\n                break;\n            case BooheeRuler.RIGHT_LAYOUT:\n                startPoint.x = width - length;\n                startPoint.y = locationY;\n                stopPoint.x = width;\n                stopPoint.y = locationY;\n                break;\n            case BooheeRuler.BOTTOM_LAYOUT:\n                startPoint.x = locationX;\n                startPoint.y = height - length;\n                stopPoint.x = locationX;\n                stopPoint.y = height;\n                break;\n            default:\n        }\n        return new PointF[]{startPoint, stopPoint};\n    }\n\n    private static PointF[] getOutLine(View contentView, int flag, int height, int width) {\n        PointF startPoint = new PointF();\n        PointF stopPoint = new PointF();\n        float locationY = 0, locationX = 0;\n        if (isVerticalRuler(flag)) {\n            //边界线是竖直的 竖直滚动的起始位置\n            locationY = contentView.getScrollY();\n        } else {\n            //边界线是水平的 水平滚动的起始位置\n            locationX = contentView.getScrollX();\n        }\n        switch (flag) {\n            case BooheeRuler.LEFT_LAYOUT:\n                startPoint.y = locationY;\n                stopPoint.y = locationY + height;\n                break;\n            case BooheeRuler.TOP_LAYOUT:\n                startPoint.x = locationX;\n                stopPoint.x = locationX + width;\n                break;\n            case BooheeRuler.RIGHT_LAYOUT:\n                startPoint.y = locationY;\n                stopPoint.y = locationY + height;\n                startPoint.x = width - 0.1F;\n                stopPoint.x = width - 0.1F;\n                break;\n            case BooheeRuler.BOTTOM_LAYOUT:\n                startPoint.x = locationX;\n                startPoint.y = height - 0.1F;\n                stopPoint.x = locationX + width;\n                stopPoint.y = height - 0.1F;\n                break;\n            default:\n        }\n        return new PointF[]{startPoint, stopPoint};\n    }\n\n    private static PointF[] getOutLeftOrRightLine(View contentView, int flag, boolean isleft, int height, int width) {\n        PointF startPoint = new PointF();\n        PointF stopPoint = new PointF();\n        float locationY = 0, locationX = 0;\n        if (isVerticalRuler(flag)) {\n            //边界线是竖直的 竖直滚动的起始位置\n            locationY = contentView.getScrollY();\n        } else {\n            //边界线是水平的 水平滚动的起始位置\n            locationX = contentView.getScrollX();\n        }\n        switch (flag) {\n            case BooheeRuler.LEFT_LAYOUT:\n                if (isleft) {\n                    startPoint.y = locationY;\n                    stopPoint.y = locationY;\n                    stopPoint.x = width;\n                } else {\n                    startPoint.y = locationY + height - 0.1F;\n                    stopPoint.y = locationY + height - 0.1F;\n                    stopPoint.x = width;\n                }\n                break;\n            case BooheeRuler.TOP_LAYOUT:\n                if (isleft) {\n                    startPoint.x = locationX;\n                    stopPoint.x = locationX;\n                    stopPoint.y = height;\n                } else {\n                    startPoint.x = locationX + width - 0.1F;\n                    stopPoint.x = locationX + width - 0.1F;\n                    stopPoint.y = height;\n                }\n                break;\n            case BooheeRuler.RIGHT_LAYOUT:\n                if (isleft) {\n                    startPoint.y = locationY;\n                    stopPoint.y = locationY;\n                    startPoint.x = width - 0.1F;\n                } else {\n                    startPoint.y = locationY + height - 0.1F;\n                    stopPoint.y = locationY + height - 0.1F;\n                    startPoint.x = width - 0.1F;\n                }\n                break;\n            case BooheeRuler.BOTTOM_LAYOUT:\n                if (isleft) {\n                    startPoint.x = locationX;\n                    startPoint.y = height - 0.1F;\n                    stopPoint.x = locationX;\n                } else {\n                    startPoint.x = locationX + width - 0.1F;\n                    startPoint.y = height - 0.1F;\n                    stopPoint.x = locationX + width - 0.1F;\n                }\n                break;\n            default:\n        }\n        return new PointF[]{startPoint, stopPoint};\n    }\n\n    //======滑动事件逻辑======\n    //获取控件宽高，设置相应信息\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        mLength = (mParent.getMaxScale() - mParent.getMinScale()) * mParent.getInterval();\n        int mHalf = (isVerticalRuler(flag) ? h : w) / 2;\n        mMinPosition = -mHalf;\n        mMaxPosition = mLength - mHalf;\n    }\n\n    @Override\n    public boolean dispatchTouchEvent(MotionEvent event) {\n        getParent().requestDisallowInterceptTouchEvent(true);\n        return super.dispatchTouchEvent(event);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n\n        float currentY = event.getY();\n        float currentX = event.getX();\n        if (mVelocityTracker == null) {\n            mVelocityTracker = VelocityTracker.obtain();\n        }\n        mVelocityTracker.addMovement(event);\n\n        switch (event.getAction()) {\n            case MotionEvent.ACTION_DOWN:\n                if (!mOverScroller.isFinished()) {\n                    mOverScroller.abortAnimation();\n                }\n                mLastY = currentY;\n                mLastX = currentX;\n                break;\n            case MotionEvent.ACTION_MOVE:\n                float moveX = isVerticalRuler(flag) ? 0 : mLastX - currentX;\n                float moveY = isVerticalRuler(flag) ? mLastY - currentY : 0;\n                scrollBy((int) moveX, (int) moveY);\n                mLastY = currentY;\n                mLastX = currentX;\n                break;\n            case MotionEvent.ACTION_UP:\n                mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);\n                int velocityY = 0, velocityX = 0;\n                boolean startScroll = false;\n                if (isVerticalRuler(flag)) {\n                    velocityY = (int) mVelocityTracker.getYVelocity();\n                    startScroll = Math.abs(velocityY) > mMinimumVelocity;\n                } else {\n                    velocityX = (int) mVelocityTracker.getXVelocity();\n                    startScroll = Math.abs(velocityX) > mMinimumVelocity;\n                }\n                if (startScroll) {\n                    fling(isVerticalRuler(flag), -velocityX, -velocityY);\n                } else {\n                    scrollBackToCurrentScale(isVerticalRuler(flag));\n                }\n\n                //VelocityTracker回收\n                if (mVelocityTracker != null) {\n                    mVelocityTracker.recycle();\n                    mVelocityTracker = null;\n                }\n                //releaseEdgeEffects();\n                break;\n            case MotionEvent.ACTION_CANCEL:\n                if (!mOverScroller.isFinished()) {\n                    mOverScroller.abortAnimation();\n                }\n                scrollBackToCurrentScale(isVerticalRuler(flag));\n                //VelocityTracker回收\n                if (mVelocityTracker != null) {\n                    mVelocityTracker.recycle();\n                    mVelocityTracker = null;\n                }\n                //releaseEdgeEffects();\n                break;\n            default:\n        }\n        return true;\n    }\n\n    private void fling(boolean isVertical, int vx, int vy) {\n        int startX = 0, startY = 0, velocityX = 0, velocityY = 0, minX = 0, minY = 0, maxX = 0, maxY = 0;\n        startX = isVertical ? 0 : getScrollX();\n        startY = isVertical ? getScrollY() : 0;\n        velocityX = isVertical ? 0 : vx;\n        velocityY = isVertical ? vy : 0;\n        int min = mMinPosition - mEdgeLength;\n        int max = mMaxPosition + mEdgeLength;\n        minX = isVertical ? 0 : min;\n        minY = isVertical ? 0 : max;\n        maxX = isVertical ? min : 0;\n        maxY = isVertical ? max : 0;\n        mOverScroller.fling(startX, startY, velocityX, velocityY, minX, minY, maxX, maxY);\n        invalidate();\n    }\n\n    @Override\n    public void computeScroll() {\n        if (mOverScroller.computeScrollOffset()) {\n            scrollTo(mOverScroller.getCurrX(), mOverScroller.getCurrY());\n            if (!mOverScroller.computeScrollOffset() && mCurrentScale != Math.round(mCurrentScale)) {\n                scrollBackToCurrentScale(isVerticalRuler(flag));\n            }\n            invalidate();\n        }\n    }\n\n\n    protected void scrollBackToCurrentScale(boolean isVertical) {\n        int startX = 0, startY = 0, destX, destY = 0;\n        startX = isVertical ? 0 : getScrollX();\n        startY = isVertical ? getScrollY() : 0;\n        destX = isVertical ? 0 : scale2ScrollXY(Math.round(mCurrentScale)) - startX;\n        destY = isVertical ? scale2ScrollXY(Math.round(mCurrentScale)) - startY : 0;\n        mOverScroller.startScroll(startX, startY, destX, destY, 1000);\n        invalidate();\n    }\n\n    protected void scroll2Scale(float scale) {\n        mCurrentScale = Math.round(scale);\n        scrollTo(0, scale2ScrollXY(mCurrentScale));\n    }\n\n    /**\n     * 将移动过程中经过的刻度显示出来\n     *\n     * @param x\n     * @param y\n     */\n    @Override\n    public void scrollTo(@Px int x, @Px int y) {\n        if (isVerticalRuler(flag)) {\n            if (y < mMinPosition || y > mMaxPosition) {\n                y = mMinPosition;\n            }\n            if (y != getScrollY()) {\n                super.scrollTo(x, y);\n            }\n            mCurrentScale = scrollXY2Scale(y);\n        } else {\n            if (x < mMinPosition || x > mMaxPosition) {\n                x = mMinPosition;\n            }\n            if (x != getScrollX()) {\n                super.scrollTo(x, y);\n            }\n            mCurrentScale = scrollXY2Scale(x);\n        }\n        if (mRulerCallbacks != null) {\n            for (RulerCallback item :\n                    mRulerCallbacks) {\n                item.onScaleChanging(Math.round(mCurrentScale));\n            }\n        }\n    }\n\n    /**\n     * 将滚动位置转化为刻度\n     *\n     * @param scrollXY\n     * @return\n     */\n    private float scrollXY2Scale(int scrollXY) {\n        return ((float) (scrollXY - mMinPosition) / mLength) * mMaxLength + mParent.getMinScale();\n    }\n\n    /**\n     * 将刻度转化为移动的位置\n     *\n     * @param scale\n     * @return\n     */\n    private int scale2ScrollXY(float scale) {\n        return (int) ((scale - mParent.getMinScale()) / mMaxLength * mLength + mMinPosition);\n    }\n}\n"
  },
  {
    "path": "ruler/src/main/java/com/keyboard3/RulerCallback.java",
    "content": "package com.keyboard3;\n\n/**\n * Created by yany on 2017/10/16.\n */\n\npublic interface RulerCallback {\n    //选取刻度变化的时候回调\n    void onScaleChanging(float scale);\n    //选取刻度变化完成的时候回调\n//    void afterScaleChanged(float scale);\n}\n"
  },
  {
    "path": "ruler/src/main/java/com/keyboard3/RulerNumberLayout.java",
    "content": "package com.keyboard3;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.support.annotation.ColorInt;\nimport android.support.annotation.IdRes;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\n\n/**\n * Created by yany on 2017/10/17.\n * 用于包着显示具体数字刻度Layout\n */\n\npublic class RulerNumberLayout extends RelativeLayout implements RulerCallback {\n    private TextView tv_scale, tv_kg;\n    //字体大小\n    private float mScaleTextSize = 80, mKgTextSize = 40;\n    //字体颜色\n    private @ColorInt\n    int mScaleTextColor = getResources().getColor(R.color.colorForgiven);\n    private @ColorInt\n    int mKgTextColor = getResources().getColor(R.color.colorForgiven);\n    @IdRes\n    int mTargetRuler;\n    //kg单位文字\n    private String mUnitText = \"kg\";\n\n\n    public RulerNumberLayout(Context context) {\n        super(context);\n        init(context);\n    }\n\n    public RulerNumberLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        initAttrs(context, attrs);\n        init(context);\n    }\n\n    public RulerNumberLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context);\n    }\n\n    private void initAttrs(Context context, AttributeSet attrs) {\n        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RulerNumberLayout, 0, 0);\n        mScaleTextSize = typedArray.getDimension(R.styleable.RulerNumberLayout_scaleTextSize, mScaleTextSize);\n        mKgTextSize = typedArray.getDimension(R.styleable.RulerNumberLayout_kgTextSize, mKgTextSize);\n        mScaleTextColor = typedArray.getColor(R.styleable.RulerNumberLayout_scaleTextColor, mScaleTextColor);\n        mKgTextColor = typedArray.getColor(R.styleable.RulerNumberLayout_kgTextColor, mKgTextColor);\n        mKgTextColor = typedArray.getColor(R.styleable.RulerNumberLayout_kgTextColor, mKgTextColor);\n        mTargetRuler = typedArray.getResourceId(R.styleable.RulerNumberLayout_targetRuler, 0);\n        String text = typedArray.getString(R.styleable.RulerNumberLayout_kgUnitText);\n        if (text != null) {\n            mUnitText = text;\n        }\n        typedArray.recycle();\n\n    }\n\n    private void init(Context context) {\n        LayoutInflater.from(context).inflate(R.layout.layout_kg_number, this);\n        tv_scale = (TextView) findViewById(R.id.tv_scale);\n        tv_kg = (TextView) findViewById(R.id.tv_kg);\n\n        tv_scale.setTextSize(TypedValue.COMPLEX_UNIT_PX, mScaleTextSize);\n        tv_scale.setTextColor(mScaleTextColor);\n\n        tv_kg.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKgTextSize);\n        tv_kg.setTextColor(mKgTextColor);\n        tv_kg.setText(mUnitText);\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        if (mTargetRuler != 0) {\n            View ruler = getRootView().findViewById(mTargetRuler);\n            if (ruler instanceof BooheeRuler) {\n                ((BooheeRuler) ruler).addCallback(this);\n            }\n        }\n\n    }\n\n    public void bindRuler(BooheeRuler booheeRuler) {\n        booheeRuler.addCallback(this);\n    }\n\n    @Override\n    public void onScaleChanging(float scale) {\n        tv_scale.setText(String.valueOf(scale / 10));\n    }\n}"
  },
  {
    "path": "ruler/src/main/res/drawable/cursor_shape.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"@color/colorForgiven\" />\n    <corners android:radius=\"2dp\"/>\n\n</shape>"
  },
  {
    "path": "ruler/src/main/res/layout/layout_kg_number.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=\"wrap_content\">\n\n    <TextView\n        android:id=\"@+id/tv_scale\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\" />\n\n    <TextView\n        android:id=\"@+id/tv_kg\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignTop=\"@+id/tv_scale\"\n        android:layout_marginLeft=\"6dp\"\n        android:layout_toRightOf=\"@+id/tv_scale\"\n        android:text=\"kg\" />\n</RelativeLayout>"
  },
  {
    "path": "ruler/src/main/res/values/attr.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <declare-styleable name=\"BooheeRuler\">\n        <attr name=\"minScale\" format=\"integer\" />\n        <attr name=\"maxScale\" format=\"integer\" />\n        <attr name=\"smallScaleLength\" format=\"dimension\" />\n        <attr name=\"smallScaleWidth\" format=\"dimension\" />\n        <attr name=\"bigScaleLength\" format=\"dimension\" />\n        <attr name=\"bigScaleWidth\" format=\"dimension\" />\n        <attr name=\"cursorHeight\" format=\"dimension\" />\n        <attr name=\"cursorWidth\" format=\"dimension\" />\n        <attr name=\"textMarginHead\" format=\"dimension\" />\n        <attr name=\"numberTextSize\" format=\"dimension\" />\n        <attr name=\"scaleInterval\" format=\"dimension\" />\n        <attr name=\"numberTextColor\" format=\"color\" />\n        <attr name=\"scaleColor\" format=\"color\" />\n        <attr name=\"cursorDrawable\" format=\"dimension\" />\n        <attr name=\"targetRulerNumber\" format=\"reference\" />\n        <attr name=\"currentScale\" format=\"float\" />\n        <attr name=\"paddingStartAndEnd\" format=\"dimension\" />\n        <attr name=\"count\" format=\"integer\" />\n        <attr name=\"rulerStyle\" format=\"enum\">\n            <enum name=\"left\" value=\"0\" />\n            <enum name=\"top\" value=\"1\" />\n            <enum name=\"right\" value=\"2\" />\n            <enum name=\"bottom\" value=\"3\" />\n        </attr>\n        <attr name=\"rulerBackGround\" format=\"reference|color\" />\n        <attr name=\"canEdgeEffect\" format=\"boolean\" />\n        <attr name=\"edgeColor\" format=\"color\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"RulerNumberLayout\">\n        <attr name=\"scaleTextSize\" format=\"dimension\" />\n        <attr name=\"kgTextSize\" format=\"dimension\" />\n        <attr name=\"scaleTextColor\" format=\"color\" />\n        <attr name=\"kgTextColor\" format=\"color\" />\n        <attr name=\"kgUnitText\" format=\"string\" />\n        <attr name=\"targetRuler\" format=\"reference\" />\n    </declare-styleable>\n</resources>"
  },
  {
    "path": "ruler/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorGray\">#e2e5e2</color>\n    <color name=\"colorDirtyWithe\">#f6f9f6</color>\n    <color name=\"colorForgiven\">#4bbb74</color>\n    <color name=\"colorLightBlack\">#2B2E2B</color>\n</resources>"
  },
  {
    "path": "ruler/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">Ruler</string>\n</resources>\n"
  },
  {
    "path": "ruler/src/test/java/com/keyboard3/ExampleUnitTest.java",
    "content": "package com.keyboard3;\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": "settings.gradle",
    "content": "include ':app',  ':ruler'\n"
  }
]