Repository: keyboard3/HencoderKeyboard3
Branch: master
Commit: 2de932b174fd
Files: 52
Total size: 106.2 KB
Directory structure:
gitextract_oi1ooax2/
├── .gitignore
├── README.md
├── app/
│ ├── .gitignore
│ ├── build/
│ │ └── outputs/
│ │ └── apk/
│ │ └── debug/
│ │ └── app-debug.apk
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── keyboard3/
│ │ └── hencoderProduct/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── keyboard3/
│ │ │ └── hencoderProduct/
│ │ │ ├── MainActivity.java
│ │ │ ├── PageFragment.java
│ │ │ ├── Utils.java
│ │ │ ├── filpboard/
│ │ │ │ ├── FlipboardLayout.java
│ │ │ │ └── FlipboardView.java
│ │ │ ├── like/
│ │ │ │ ├── LikeImageView.java
│ │ │ │ ├── LikeLayout.java
│ │ │ │ ├── LikeNumView.java
│ │ │ │ └── LikeView.java
│ │ │ ├── movement/
│ │ │ │ ├── MoveLayout.java
│ │ │ │ └── MoveView.java
│ │ │ └── ruler/
│ │ │ └── RulerLayout.java
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── filpboard_layout.xml
│ │ │ ├── fragment_page.xml
│ │ │ ├── like_layout.xml
│ │ │ ├── move_layout.xml
│ │ │ └── ruler_layout.xml
│ │ └── values/
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── com/
│ └── keyboard3/
│ └── hencoderProduct/
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── ruler/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── keyboard3/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── keyboard3/
│ │ │ ├── BooheeRuler.java
│ │ │ ├── InnerRuler.java
│ │ │ ├── RulerCallback.java
│ │ │ └── RulerNumberLayout.java
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── cursor_shape.xml
│ │ ├── layout/
│ │ │ └── layout_kg_number.xml
│ │ └── values/
│ │ ├── attr.xml
│ │ ├── colors.xml
│ │ └── strings.xml
│ └── test/
│ └── java/
│ └── com/
│ └── keyboard3/
│ └── ExampleUnitTest.java
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
/.idea/
================================================
FILE: README.md
================================================
# HencoderKeyboard3
[HenCoder「仿写酷界面」活动——征稿](http://hencoder.com/activity-mock-1) <br>
[HenCoder「仿写酷界面」活动——获奖作品点评](http://hencoder.com/activity-mock-2/)<br>
虽然4个作品均未命中奖项,但本人还是会持续优化....,敬请关注!!!
- 写作背景<br>
在这个作品之前都是比较零散的搞一个比较好的效果仿写,比如我之前仿写 [QQ 健康效果](https://github.com/keyboard3/SelfView)。 hencoder 这个系列很完整,比之其他博主写的形式更加通俗易懂,我觉得这些都是次要的,都是知识获取渠道,最重要的是将知识落地。这场比赛我觉得很好,投稿、点评很能够调用一个开发者的兴趣。说实话在邀请投稿文章出来之后,自定义 View 经验不是很丰富的我居然在十几个小时就连续将4个作品全部仿写出来投稿了,可以说是调动了我很高的兴趣和潜力。当然作品还是有很多欠缺的地方,我会不断的进行完善。
## 下载
[demo.apk](app/build/outputs/apk/debug/app-debug.apk)
# 即刻点赞
- 写在前面
获奖作品[ThumbUpSample](https://github.com/arvinljw/ThumbUpSample)<br>
```
在揭晓优胜者之后我对比了实现原理,基本原理就是左边炫丽的效果和右边文字上下滚动分离。我在左边的效果实现上忘记了一个光圈的缩放,当时以为是鼠标自己的效果....,比较遗憾!
```
- 实现原理
```
实现原理:隔离并复用图片动画和文字动画
LikeView自定义的LinearLayout默认组合点赞文字动画效果
LikeImageView
点赞图片动画
点赞效果
灰色的点赞图标变小至0.9倍
转变成红色的图标并且半透明
红色图标逐渐增长至正常的1.1倍 / 透明度在增长至正常时逐渐变成实体 最后变成正常大小的红色图标
点赞伴随动画光圈
0.6倍点赞图标大小的光圈、半透明
0-50%动画完成度时半透明变成实体
50%-100%动画完成度时实体又逐渐变成透明
光圈半径逐渐增大至1.1倍
取消点赞效果
红色图标变小至0.9倍且变成半透明 动画完成到一半时变成灰色的正常大小
闪光动画
点赞时 闪光图标在点赞图标顶部的某个位置,先由小到大直至正常大小
LikeNumView
点赞和取消赞动作导致的文字变化 转变成 原数字->新数字。点赞和取消赞时改动新数字的值(+1/-1)。将两个数字动转为字符串数组,从高位开始循环 如果数字相同就直接画数字,如果数字不同就开始绘制两个数字位移同时设置对应的透明渐变`
```
- 对比获奖作品
```
获奖作品之后并没有多大改动
点赞散开点并没有做处理
我主要是在我原来的基础上将基本特效都实现了,包括点赞散开的效果
```
- 截图
<img src="images/like.gif" width="350"><br>
# 薄荷健康尺
- 写在前面
获奖作品[BooheeRuler](https://github.com/totond/BooheeRuler)<br>
```
在揭晓获奖作品之后,看了当时的实现方式原理一致,中央高亮刻度覆盖尺子之上,尺子刻度值全部绘制出来,然后滑动内容。因为当时滑动交互算是hencoder的超纲内容,因而漏写了这一块很遗憾!
```
- 实现原理
```
分析:
此控件分两块,下方尺子和上方显示的中央刻度值。下方尺子中刻度内容可以滑动而在其上的中央高亮刻度固定。由上分析:将此控件分成两个view,rulerView和rulerNumerView
rulerView:刻度要可以滑动即刻度是一个独立的View,上方固定的中央刻度则是ViewGroup在刻度子View绘制完成之后在上面覆盖绘制一个高亮的刻度线
rulerNumberView:实时监听rulerView的刻度滚动经过中央高亮刻度线时的值显示
```
- 对比获奖作品
```
获奖作品在众星捧月中不断进步,截止0.1.3版本
功能上:实现了尺子的左上右下的四种显示方向
性能上:每次重绘只绘制当前显示部分刻度
我觉得它已经做的很完美了,但是我觉得还是有些补足之处的。
1.实现四种显示方向结构太臃肿、完全可以直接在InnerRuler中绘制刻度和文字等处做方向处理调整绘制坐标
2.监听刻度值的View可以做的更加解耦一些,rulerView和rulerNumberView可以做到n:n
我这里在他的作品之上做了上面我所说的改造,因为时间关系我阉割了他边界阴影效果
```
- 截图
<img src="images/ruler.gif" width="350">
## 小米运动
获奖作品[MISportsConnectWidget](https://github.com/sickworm/MISportsConnectWidget)
- 写在前面
```
这个作品难点在于喷射的效果,因为经验较少直接采用了笨办法,稍微处理了一下随机算法。
最后看到有人实现这个效果之后真心佩服,原作者点评也很专业
```
- 实现原理
```
带粒子喷射的头部以及虚化线尾巴旋转效果
最终页面显示结果的外光晕旋转
```
- 作品对比
```
相形见绌啊,只讲我的缺点吧
粒子喷射效果没有已随机点击代替...
最终结果的外圈旋转光晕没有...
```
- 截图
<img src="images/miMove.gif" width="350">
## Fliboard 翻页效果
获奖作品[HenCoderPractice](https://github.com/sunnyxibei/HenCoderPractice)
- 写在前面
```
这个是4个中我最先写出来的,稿子发出来之后,下班之前就发了出来。起初有点懵,当效果分拆成小块小块的时候,发现很容易实现,最后拼起来效果一致。
当这个作品没获奖的时候真的比较遗憾,可能是输给细节吧!
```
- 实现原理
```
将效果进行拆分成启动右侧的翻折、旋转翻折、结尾下方也翻折
难点在于如果实现旋转翻折,翻折部分和正常部分分开绘制。
带着翻折部分旋转:裁剪出翻折部分的canvas,将这部分canvs依照整体做旋转,然后将图片绘制在上面,最后反向旋转回来然后将这部分与正常部分拼接就完成了整个效果
```
- 作品对比
```
差异不大,获奖者比较鸡贼,图片采用他们最新的图标....
```
- 截图
<img src="images/flipboard.gif" width="350">
## 关于我
简书 [keyboard3](http://www.jianshu.com/users/62329de8c8a6/latest_articles)<br>
邮箱 keyboard3@icloud.com
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion '26.0.2'
defaultConfig {
applicationId "com.keyboard3.hencoderProduct"
minSdkVersion 18
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
apply plugin: 'replugin-plugin-gradle'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.qihoo360.replugin:replugin-plugin-lib:2.2.0'
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:support-core-ui:25.3.1'
compile 'com.android.support:design:25.3.1'
testCompile 'junit:junit:4.12'
compile project(':ruler')
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/rengwuxian/.android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: app/src/androidTest/java/com/keyboard3/hencoderProduct/ExampleInstrumentedTest.java
================================================
package com.keyboard3.hencoderProduct;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.hencoder.hencoderpracticedraw5", appContext.getPackageName());
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.keyboard3.hencoderProduct">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.keyboard3.hencoderProduct.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/MainActivity.java
================================================
package com.keyboard3.hencoderProduct;
import android.os.Bundle;
import android.os.Handler;
import android.os.MessageQueue;
import android.support.annotation.LayoutRes;
import android.support.annotation.StringRes;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
TabLayout tabLayout;
ViewPager pager;
List<PageModel> pageModels = new ArrayList<>();
{
pageModels.add(new PageModel(R.layout.like_layout, R.string.title_like));
pageModels.add(new PageModel(R.layout.ruler_layout, R.string.title_ruler));
pageModels.add(new PageModel(R.layout.move_layout, R.string.title_move));
pageModels.add(new PageModel(R.layout.filpboard_layout, R.string.title_flipboard));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
PageModel pageModel = pageModels.get(position);
return PageFragment.newInstance(pageModel.productLayoutRes);
}
@Override
public int getCount() {
return pageModels.size();
}
@Override
public CharSequence getPageTitle(int position) {
return getString(pageModels.get(position).titleRes);
}
});
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
tabLayout.setupWithViewPager(pager);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return super.onCreateOptionsMenu(menu);
}
private class PageModel {
@LayoutRes
int productLayoutRes;
@StringRes
int titleRes;
PageModel(@LayoutRes int sampleLayoutRes, @StringRes int titleRes) {
this.productLayoutRes = sampleLayoutRes;
this.titleRes = titleRes;
}
}
}
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/PageFragment.java
================================================
package com.keyboard3.hencoderProduct;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
public class PageFragment extends Fragment {
@LayoutRes
int productLayoutRes;
public static PageFragment newInstance(@LayoutRes int productLayoutRes) {
PageFragment fragment = new PageFragment();
Bundle args = new Bundle();
args.putInt("productLayoutRes", productLayoutRes);
fragment.setArguments(args);
return fragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_page, container, false);
ViewStub sampleStub = (ViewStub) view.findViewById(R.id.sampleStub);
sampleStub.setLayoutResource(productLayoutRes);
sampleStub.inflate();
return view;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
productLayoutRes = args.getInt("productLayoutRes");
}
}
}
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/Utils.java
================================================
package com.keyboard3.hencoderProduct;
import android.content.res.Resources;
import android.util.DisplayMetrics;
public class Utils {
public static float dpToPixel(float dp) {
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
return dp * metrics.density;
}
}
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/filpboard/FlipboardLayout.java
================================================
package com.keyboard3.hencoderProduct.filpboard;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.RelativeLayout;
import com.keyboard3.hencoderProduct.R;
public class FlipboardLayout extends RelativeLayout {
FlipboardView view;
Button animateBt;
public FlipboardLayout(Context context) {
super(context);
}
public FlipboardLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FlipboardLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
view = (FlipboardView) findViewById(R.id.objectAnimatorView);
animateBt = (Button) findViewById(R.id.animateBt);
animateBt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
view.clearCanvas();
ObjectAnimator animator1 = ObjectAnimator.ofInt(view, "turnoverDegreeFirst", 0, 30);
animator1.setDuration(1000);
animator1.setInterpolator(new LinearInterpolator());
ObjectAnimator animator2 = ObjectAnimator.ofInt(view, "degree", 0, 270);
animator2.setDuration(2000);
animator2.setInterpolator(new AccelerateDecelerateInterpolator());
ObjectAnimator animator3 = ObjectAnimator.ofInt(view, "turnoverDegreeLast", 0, 30);
animator3.setDuration(1000);
animator3.setInterpolator(new LinearInterpolator());
AnimatorSet animatorSet = new AnimatorSet();
// 两个动画依次执行
animatorSet.playSequentially(animator1, animator2, animator3);
animatorSet.start();
}
});
}
}
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/filpboard/FlipboardView.java
================================================
package com.keyboard3.hencoderProduct.filpboard;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import com.keyboard3.hencoderProduct.R;
/**
* @author keyboard3
*/
public class FlipboardView extends View {
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Camera mCamera = new Camera();
private Bitmap mBitmap;
private int turnoverDegreeFirst;
private int turnoverDegreeLast;
private int degree;
public FlipboardView(Context context) {
super(context);
}
public FlipboardView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public FlipboardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.filpboard);
}
public void clearCanvas() {
degree = 0;
turnoverDegreeFirst = 0;
turnoverDegreeLast = 0;
invalidate();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int bitmapWidth = mBitmap.getWidth();
int bitmapHeight = mBitmap.getHeight();
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
int x = centerX - bitmapWidth / 2;
int y = centerY - bitmapHeight / 2;
//绘制上半部分
canvas.save();
canvas.rotate(-degree, centerX, centerY);
canvas.clipRect(0, 0, centerX, getHeight());
canvas.rotate(degree, centerX, centerY);
//翻折画布
mCamera.save();
mCamera.rotateX(-turnoverDegreeLast);
canvas.translate(centerX, centerY);
mCamera.applyToCanvas(canvas);
canvas.translate(-centerX, -centerY);
mCamera.restore();
canvas.drawBitmap(mBitmap, x, y, mPaint);
canvas.restore();
//绘制右边部分
canvas.save();
canvas.rotate(-degree, centerX, centerY);
canvas.clipRect(centerX, 0, getWidth(), getHeight());
//翻折画布
mCamera.save();
mCamera.rotateY(-turnoverDegreeFirst);
canvas.translate(centerX, centerY);
mCamera.applyToCanvas(canvas);
canvas.translate(-centerX, -centerY);
mCamera.restore();
canvas.rotate(degree, centerX, centerY);
//向画布绘制内容
canvas.drawBitmap(mBitmap, x, y, mPaint);
canvas.restore();
}
@SuppressWarnings("unused")
public void setDegree(int degree) {
this.degree = degree;
invalidate();
}
@SuppressWarnings("unused")
public void setTurnoverDegreeLast(int turnoverDegreeLast) {
this.turnoverDegreeLast = turnoverDegreeLast;
invalidate();
}
@SuppressWarnings("unused")
public void setTurnoverDegreeFirst(int turnoverDegreeFirst) {
this.turnoverDegreeFirst = turnoverDegreeFirst;
invalidate();
}
}
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/like/LikeImageView.java
================================================
package com.keyboard3.hencoderProduct.like;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
/**
* @author keyboard3
* @date 2017/10/30
*/
public class LikeImageView extends View {
private Paint mImagePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private boolean liked = false;
private float animProgress;
private Bitmap mUnlikeBitmap;
private Bitmap mLikedBitmap;
private Bitmap mShiningBitmap;
private float leftPadding;
private float middlePadding;
private float startX;
private int centerY;
public LikeImageView(Context context) {
super(context);
}
public LikeImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public LikeImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
mImagePaint.setColor(Color.parseColor("#c3c4c3"));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
startX = mLikedBitmap.getWidth() * 0.1f + leftPadding;
int width = (int) (mLikedBitmap.getWidth() * 1.1);
int height = mLikedBitmap.getHeight() + mShiningBitmap.getHeight();
setMeasuredDimension((int) (width + leftPadding + middlePadding), height);
}
public void setLike(boolean isLike) {
liked = isLike;
if (!liked) {
animProgress = 0;
}
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
centerY = getHeight() / 2;
//绘制放大圈
drawCircle(canvas, (int) (startX + mLikedBitmap.getWidth() / 2), centerY);
//绘制点赞图片
int likeTop = centerY - mUnlikeBitmap.getHeight() / 2;
drawLike(canvas, likeTop, (int) startX);
//绘制闪光
drawShining(canvas, likeTop, (int) startX);
}
private void drawCircle(Canvas canvas, int centerX, int centerY) {
float radius = 0;
int alpha = 0;
if (liked&&animProgress > 0) {
//透明变实体
if (animProgress > 0 && animProgress <= 0.5) {
alpha = (int) (255 * (0.5 + animProgress));
} else { //实体变透明
alpha = (int) (255 * (1 - (animProgress - 0.5) * 2));
}
radius = (float) (0.6 + animProgress * 0.7);
}
mImagePaint.setColor(Color.parseColor("#cc775c"));
mImagePaint.setAlpha(alpha);
mImagePaint.setStyle(Paint.Style.STROKE);
mImagePaint.setStrokeWidth(3);
canvas.drawCircle(centerX, centerY, radius * mLikedBitmap.getWidth() / 2, mImagePaint);
mImagePaint.setColor(Color.parseColor("#c3c4c3"));
mImagePaint.setStyle(Paint.Style.FILL);
}
private void drawLike(Canvas canvas, int likeTop, int likeLeft) {
Bitmap bitmap = null;
float scale = 0;
if (liked) {
//灰色一下变小
if (animProgress > 0 && animProgress <= 0.1) {
bitmap = mUnlikeBitmap;
scale = -0.01f;
}
//红色小且半透明 变正常过程就变成了实体
if (animProgress > 0.1 && animProgress <= 0.5) {
mImagePaint.setAlpha((int) (255 * (0.5 + animProgress)));
} else {
mImagePaint.setAlpha(255);
}
//红色放大
if (animProgress > 0.1 && animProgress <= 0.9) {
bitmap = mLikedBitmap;
scale = (float) (-0.01f + animProgress * 0.1);
}
//一瞬间变正常
if (animProgress > 0.9 || animProgress == 0) {
bitmap = mLikedBitmap;
scale = 0;
}
} else {
//红色缩小 变半透明
if (animProgress > 0 && animProgress <= 0.5) {
bitmap = mLikedBitmap;
mImagePaint.setAlpha((int) (255 * (0.5 + animProgress)));
scale = (float) (-animProgress * 0.1);
}
//一半的时候变灰色
if (animProgress > 0.5 || animProgress == 0) {
mImagePaint.setAlpha(255);
bitmap = mUnlikeBitmap;
scale = 0;
}
}
canvas.save();
canvas.translate((float) (likeLeft - bitmap.getWidth() * 0.05), (float) (likeTop - bitmap.getHeight() * 0.05));
canvas.scale(1 + scale, 1 + scale);
canvas.drawBitmap(bitmap, 0, 0, mImagePaint);
canvas.restore();
}
private void drawShining(Canvas canvas, int likeTop, int likeLeft) {
if (liked&&animProgress > 0) {
float scale = animProgress;
int shiningTop = (int) (likeTop - scale * mShiningBitmap.getHeight() / 2);
int shiningLeft = (int) (likeLeft + (mLikedBitmap.getWidth() - scale * mShiningBitmap.getWidth()) / 2);
canvas.save();
canvas.translate(shiningLeft, shiningTop);
canvas.scale(scale, scale);
canvas.drawBitmap(mShiningBitmap, 0, 0, mImagePaint);
canvas.restore();
}
}
@SuppressWarnings("unused")
public void setAnimProgress(float animProgress) {
this.animProgress = animProgress;
invalidate();
}
public void setMiddlePadding(float middlePadding) {
this.middlePadding = middlePadding;
}
public void setLeftPadding(float leftPadding) {
this.leftPadding = leftPadding;
}
public void setUnlikeSrc(@IdRes int unlikeSrc) {
mUnlikeBitmap = BitmapFactory.decodeResource(getResources(), unlikeSrc);
}
public void setLikedSrc(@IdRes int likeSrc) {
mLikedBitmap = BitmapFactory.decodeResource(getResources(), likeSrc);
}
public void setShiningdSrc(@IdRes int shiningSrc) {
mShiningBitmap = BitmapFactory.decodeResource(getResources(), shiningSrc);
}
}
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/like/LikeLayout.java
================================================
package com.keyboard3.hencoderProduct.like;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import com.keyboard3.hencoderProduct.R;
public class LikeLayout extends RelativeLayout {
LikeView view1, view2, view3;
Button animateBt;
public LikeLayout(Context context) {
super(context);
}
public LikeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LikeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
view1 = (LikeView) findViewById(R.id.objectAnimatorView1);
view2 = (LikeView) findViewById(R.id.objectAnimatorView2);
view3 = (LikeView) findViewById(R.id.objectAnimatorView3);
animateBt = (Button) findViewById(R.id.animateBt);
animateBt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
view1.performClick();
view2.performClick();
view3.performClick();
}
});
}
}
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/like/LikeNumView.java
================================================
package com.keyboard3.hencoderProduct.like;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import com.keyboard3.hencoderProduct.Utils;
/**
* @author keyboard3
*/
public class LikeNumView extends View {
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int mCurNum = 0;
private int mNewNum = 0;
private int translationY;
private boolean liked = false;
private int mMoveY;
private int mTextSize;
private int centerY;
private float rightPadding;
public LikeNumView(Context context) {
super(context);
}
public LikeNumView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public LikeNumView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
mTextSize = (int) Utils.dpToPixel(12);
mPaint.setTextSize(mTextSize);
mPaint.setColor(Color.parseColor("#c3c4c3"));
}
protected void setNum(int num) {
mCurNum = mNewNum = num;
invalidate();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//保证长度足够
String curNum = (mCurNum + 1) * 10 + "";
Rect rect = new Rect();
mPaint.getTextBounds(curNum, 0, curNum.length(), rect);
float width = rect.width() + rightPadding;
int height = mTextSize * 3;
int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);
width = resolveSize((int) width, widthSpecSize);
setMeasuredDimension((int) width, height);
mMoveY = height / 2;
}
protected void changeLike(boolean isLike) {
if (isLike) {
if (mCurNum != 0) {
mNewNum = mCurNum - 1;
}
} else {
mNewNum = mCurNum + 1;
}
liked = !isLike;
}
protected void init() {
mCurNum = mNewNum;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
centerY = getHeight() / 2;
int leftX = 0;
Rect rect = new Rect();
mPaint.getTextBounds("0", 0, 1, rect);
drawAnimNum(canvas, leftX, centerY - (rect.top + rect.bottom) / 2, mCurNum, mNewNum);
}
private void drawAnimNum(Canvas canvas, int leftX, int baseTxtY, int curNum, int newNum) {
String curNumStr = (curNum + "").toString();
String newNumStr = (newNum + "").toString();
int len = Math.max(curNumStr.length(), newNumStr.length());
float charLen = mPaint.measureText("0");
int sumLeft = leftX;
String curCharTxt, newCharTxt;
for (int i = 0; i < len; i++) {
if (i > curNumStr.length() - 1) {
curCharTxt = "";
} else {
curCharTxt = curNumStr.substring(i, i + 1);
}
if (i > newNumStr.length() - 1) {
newCharTxt = "";
} else {
newCharTxt = newNumStr.substring(i, i + 1);
}
optDrawNum(canvas, sumLeft, baseTxtY, curCharTxt, newCharTxt, newNum > curNum);
sumLeft += charLen;
}
}
private void optDrawNum(Canvas canvas, int leftX, int baseTxtY, String curNum, String newNum, boolean upOrDown) {
if (curNum.equals(newNum)) {
mPaint.setAlpha(255);
canvas.drawText(curNum, leftX, baseTxtY, mPaint);
return;
}
int alpha = (int) ((1 - 1.0 * translationY / mMoveY) * 255);
int curBaseY = baseTxtY;
int newBaseY, transY;
if (upOrDown) {
transY = -translationY;
newBaseY = baseTxtY + mMoveY;
} else {//down -1
transY = translationY;
newBaseY = baseTxtY - mMoveY;
}
mPaint.setAlpha(alpha);
canvas.drawText(curNum, leftX, curBaseY + transY, mPaint);
mPaint.setAlpha(255 - alpha);
canvas.drawText(newNum, leftX, newBaseY + transY, mPaint);
mPaint.setAlpha(255);
}
public void setLiked(boolean liked) {
this.liked = liked;
}
@SuppressWarnings("unused")
public void setTranslationY(int translationY) {
this.translationY = translationY;
invalidate();
}
public void setRightPadding(float rightPadding) {
this.rightPadding = rightPadding;
}
}
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/like/LikeView.java
================================================
package com.keyboard3.hencoderProduct.like;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import com.keyboard3.hencoderProduct.R;
/**
* @author keyboard3
* @date 2017/10/30
*/
public class LikeView extends LinearLayout {
private int mAnimTime = 500;
private LikeNumView likeNumView;
private LikeImageView likeImageView;
private AnimatorSet animatorSet;
private boolean isLike = false;
public LikeView(Context context) {
super(context);
}
public LikeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LikeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final TypedArray custom = context.obtainStyledAttributes(
attrs, R.styleable.LikeView, defStyleAttr, 0);
int likeNum = custom.getInt(R.styleable.LikeView_likeNum, 0);
boolean liked = custom.getBoolean(R.styleable.LikeView_liked, false);
float leftPadding = custom.getDimension(R.styleable.LikeView_leftPadding, 0);
float middlePadding = custom.getDimension(R.styleable.LikeView_middlePadding, 0);
float rightPadding = custom.getDimension(R.styleable.LikeView_rightPadding, 0);
int likeSrc = custom.getResourceId(R.styleable.LikeView_likeSrc, R.mipmap.ic_messages_like_selected);
int unlikeSrc = custom.getResourceId(R.styleable.LikeView_unlikeSrc, R.mipmap.ic_messages_like_unselected);
int shiningSrc = custom.getResourceId(R.styleable.LikeView_shiningSrc, R.mipmap.ic_messages_like_selected_shining);
likeImageView.setShiningdSrc(shiningSrc);
likeImageView.setLikedSrc(likeSrc);
likeImageView.setUnlikeSrc(unlikeSrc);
likeNumView.setRightPadding(rightPadding);
likeImageView.setLeftPadding(leftPadding);
likeImageView.setMiddlePadding(middlePadding);
likeNumView.setNum(likeNum);
setLike(liked);
}
{
setOrientation(HORIZONTAL);
likeImageView = new LikeImageView(getContext());
addView(likeImageView);
likeNumView = new LikeNumView(getContext());
addView(likeNumView);
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (!animatorSet.isRunning()) {
likeNumView.changeLike(isLike);
isLike = !isLike;
likeImageView.setLike(isLike);
animatorSet.start();
}
}
});
}
public void setNum(int num) {
likeNumView.setNum(num);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
animatorSet.end();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int mMoveY = getMeasuredHeight() / 2;
ObjectAnimator numAnimator = ObjectAnimator.ofInt(likeNumView, "translationY", 0, mMoveY);
numAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
likeNumView.init();
}
});
numAnimator.setDuration(mAnimTime);
ObjectAnimator imageAnimator = ObjectAnimator.ofFloat(likeImageView, "animProgress", 0, 1);
imageAnimator.setDuration(mAnimTime);
animatorSet = new AnimatorSet();
animatorSet.playTogether(numAnimator, imageAnimator);
setMeasuredDimension(likeImageView.getMeasuredWidth() + likeNumView.getMeasuredWidth(), getMeasuredHeight());
}
public void setLike(boolean like) {
isLike = like;
likeNumView.setLiked(isLike);
likeImageView.setLike(isLike);
invalidate();
}
}
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/movement/MoveLayout.java
================================================
package com.keyboard3.hencoderProduct.movement;
import android.content.Context;
import android.util.AttributeSet;
import android.view.VelocityTracker;
import android.view.View;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import com.keyboard3.hencoderProduct.R;
public class MoveLayout extends RelativeLayout {
MoveView view;
Button animateBt;
public MoveLayout(Context context) {
super(context);
}
public MoveLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MoveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
view = (MoveView) findViewById(R.id.objectAnimatorView);
animateBt = (Button) findViewById(R.id.animateBt);
OnClickListener listener = new OnClickListener() {
@Override
public void onClick(View v) {
view.startAnimal();
}
};
animateBt.setOnClickListener(listener);
view.setOnClickListener(listener);
}
}
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/movement/MoveView.java
================================================
package com.keyboard3.hencoderProduct.movement;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import com.keyboard3.hencoderProduct.R;
import com.keyboard3.hencoderProduct.Utils;
import java.util.Random;
/**
* @author keyboard3
*/
public class MoveView extends View {
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private AnimatorSet mAnimatorSet = new AnimatorSet();
private boolean mDrawInner = false;
private boolean mDrawOut = false;
private boolean mDrawLoading = true;
private String mStepNum;
private String mKmNum;
private String mCalNum;
private Bitmap mWatchBitmap;
private Random mRandom;
private int transparentWhite;
private int degree = 0;
private int maxMove;
private int centerX;
private int centerY;
public MoveView(Context context) {
super(context);
}
public MoveView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MoveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
setWillNotDraw(false);
mPaint.setTextAlign(Paint.Align.CENTER);
transparentWhite = Color.parseColor("#00ffffff");
maxMove = (int) Utils.dpToPixel(50);
mWatchBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_watch);
mRandom = new Random();
mStepNum = "2274";
mKmNum = "1.5公里";
mCalNum = "34千卡";
}
public void startAnimal() {
mDrawInner = false;
mDrawOut = false;
mDrawLoading = true;
AnimatorSet animatorSet = new AnimatorSet();
ObjectAnimator animator0 = ObjectAnimator.ofInt(MoveView.this, "degree", 0, 480);
animator0.setDuration(3000);
animator0.addListener(new AnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
mDrawLoading = false;
}
});
ObjectAnimator animator1 = ObjectAnimator.ofFloat(this, "translationY", 0, -maxMove);
animator1.setDuration(500);
animator1.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mDrawOut = true;
}
});
ObjectAnimator animator2 = ObjectAnimator.ofFloat(this, "translationY", -maxMove, 0);
animator2.setDuration(500);
animator2.addListener(new AnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
mDrawInner = true;
}
});
animatorSet.playSequentially(animator0, animator1, animator2);
animatorSet.start();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mAnimatorSet.end();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() + 200);//延长内容
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
centerX = getWidth() / 2;
centerY = getHeight() / 2;
//绘制步数
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextSize(90);
mPaint.setColor(Color.parseColor("#ffffff"));
Rect stepNumRect = new Rect();
mPaint.getTextBounds(mStepNum, 0, mStepNum.length() - 1, stepNumRect);
int stepNumBaseY = centerY - (stepNumRect.top + stepNumRect.bottom) / 2;
canvas.drawText(mStepNum, centerX, stepNumBaseY, mPaint);
//绘制km
mPaint.setTextSize(24);
mPaint.setColor(Color.parseColor("#b1d6f8"));
Rect kmNumRect = new Rect();
mPaint.getTextBounds(mKmNum, 0, mKmNum.length() - 1, kmNumRect);
int kmNumBaseX = centerX - 20 - (kmNumRect.left + kmNumRect.right) / 2;
int kmNumBaseY = stepNumBaseY + 30 + kmNumRect.height();
canvas.drawText(mKmNum, kmNumBaseX, kmNumBaseY, mPaint);
//绘制卡路里
Rect calNumRect = new Rect();
mPaint.getTextBounds(mCalNum, 0, mCalNum.length() - 1, calNumRect);
int calNumBaseX = centerX + 20 + (calNumRect.left + calNumRect.right) / 2;
int calNumBaseY = stepNumBaseY + 30 + kmNumRect.height();
canvas.drawText(mCalNum, calNumBaseX, calNumBaseY, mPaint);
//绘制中间线
mPaint.setStrokeWidth(2);
int centerLineTop = kmNumBaseY - kmNumRect.height();
int centerLineBottom = centerLineTop + kmNumRect.height();
canvas.drawLine(centerX, centerLineTop, centerX, centerLineBottom, mPaint);
//绘制最底部手表
int watchX = centerX - mWatchBitmap.getWidth() / 2;
int watchY = centerLineBottom + 40;
canvas.drawBitmap(mWatchBitmap, watchX, watchY, mPaint);
//绘制刚开始的加载的旋转动画
if (mDrawLoading) {
canvas.save();
canvas.rotate(degree, centerX, centerY);
Shader mShader = new SweepGradient(centerX, centerY, transparentWhite, Color.WHITE);
mPaint.setStrokeWidth(1);
mPaint.setShader(mShader);
mPaint.setStyle(Paint.Style.STROKE);
int loadingRadius = (int) (0.65 * getMeasuredWidth() / 2);
RectF loadingCircle = new RectF(centerX - loadingRadius, centerY - loadingRadius, centerX + loadingRadius, centerY + loadingRadius);
Path loadingPath = new Path();
float loadingLeft = loadingCircle.left, loadingTop = loadingCircle.top, loadingRight = loadingCircle.right, loadingBottom = loadingCircle.bottom;
for (int i = 0; i < 20; i++) {
int value = mRandom.nextInt(25);
int sed = mRandom.nextInt(3);
loadingCircle.left = loadingLeft + value + sed;
loadingCircle.top = loadingTop + value - sed;
loadingCircle.right = loadingRight - value + sed;
loadingCircle.bottom = loadingBottom - value - sed;
loadingPath.addArc(loadingCircle, 40, 320);
}
canvas.drawPath(loadingPath, mPaint);
loadingPath.reset();
int decorPointX = centerX + loadingRadius;
int decorPointY = centerY + 5;
int tempX, tempY;
for (int i = 0; i < 10; i++) {
int value0 = mRandom.nextInt(i + 20);
int value = i * 2 + mRandom.nextInt(i + 20);
tempX = decorPointX - value0;
tempY = decorPointY - i * 2 - value;
loadingPath.addCircle(tempX, tempY, 5, Path.Direction.CCW);
}
mPaint.setStyle(Paint.Style.FILL);
canvas.drawPath(loadingPath, mPaint);
mPaint.setShader(null);
canvas.restore();
}
//绘制外轮廓
if (mDrawOut) {
int outRadius = (int) ((0.65 + 0.15 * getPercent()) * getMeasuredWidth() / 2);
mPaint.setStrokeWidth(25);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(centerX, centerY, outRadius, mPaint);
}
//绘制内轮廓
if (mDrawInner) {
mPaint.setStrokeWidth(5);
int innerRadius = (int) (0.55 * getMeasuredWidth() / 2);
int startRunAngle = -90, runedAngle = 270, startUnRunAngle = startRunAngle + runedAngle, unRunedAngle = 360 - runedAngle;
RectF innerCircle = new RectF(centerX - innerRadius, centerY - innerRadius, centerX + innerRadius, centerY + innerRadius);
PathEffect effects = new DashPathEffect(new float[]{2, 4}, 1);
Path unRunPath = new Path();
unRunPath.addArc(innerCircle, startUnRunAngle, unRunedAngle);
mPaint.setPathEffect(effects);
canvas.drawPath(unRunPath, mPaint);
mPaint.setPathEffect(null);
mPaint.setColor(Color.WHITE);
Path runedPath = new Path();
runedPath.addArc(innerCircle, startRunAngle, runedAngle);
canvas.drawPath(runedPath, mPaint);
}
}
private float getPercent() {
return Math.abs(getTranslationY()) / maxMove;
}
@Override
public void setTranslationY(float translationY) {
super.setTranslationY(translationY);
invalidate();
}
@SuppressWarnings("unused")
public void setDegree(int degree) {
this.degree = degree;
invalidate();
}
public abstract class AnimatorListener implements Animator.AnimatorListener {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
}
}
================================================
FILE: app/src/main/java/com/keyboard3/hencoderProduct/ruler/RulerLayout.java
================================================
package com.keyboard3.hencoderProduct.ruler;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.RelativeLayout;
import com.keyboard3.hencoderProduct.R;
public class RulerLayout extends RelativeLayout {
public RulerLayout(Context context) {
super(context);
}
public RulerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RulerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#f8f8f8">
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@android:color/white"
app:tabTextAppearance="@android:style/TextAppearance.Widget.TabWidget"
app:tabMode="scrollable" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
================================================
FILE: app/src/main/res/layout/filpboard_layout.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<com.keyboard3.hencoderProduct.filpboard.FlipboardLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#2B2B2B">
<com.keyboard3.hencoderProduct.filpboard.FlipboardView
android:id="@+id/objectAnimatorView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
<Button
android:id="@+id/animateBt"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_margin="8dp"
android:text="@string/animate"
android:textSize="16sp" />
</com.keyboard3.hencoderProduct.filpboard.FlipboardLayout>
================================================
FILE: app/src/main/res/layout/fragment_page.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ViewStub
android:id="@+id/sampleStub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inflatedId="@+id/model" />
</LinearLayout>
================================================
FILE: app/src/main/res/layout/like_layout.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<com.keyboard3.hencoderProduct.like.LikeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.keyboard3.hencoderProduct.like.LikeView
android:id="@+id/objectAnimatorView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="10dp"
android:background="#2B2B2B"
app:leftPadding="5dp"
app:likeNum="9"
app:likeSrc="@mipmap/ic_messages_like_selected"
app:middlePadding="5dp"
app:rightPadding="5dp"
app:shiningSrc="@mipmap/ic_messages_like_selected_shining"
app:unlikeSrc="@mipmap/ic_messages_like_unselected" />
<com.keyboard3.hencoderProduct.like.LikeView
android:id="@+id/objectAnimatorView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="10dp"
android:background="#2B2B2B"
app:leftPadding="5dp"
app:likeNum="239"
app:likeSrc="@mipmap/ic_messages_like_selected"
app:middlePadding="5dp"
app:rightPadding="5dp"
app:shiningSrc="@mipmap/ic_messages_like_selected_shining"
app:unlikeSrc="@mipmap/ic_messages_like_unselected" />
<com.keyboard3.hencoderProduct.like.LikeView
android:id="@+id/objectAnimatorView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="10dp"
android:background="#2B2B2B"
app:leftPadding="5dp"
app:likeNum="100000"
app:likeSrc="@mipmap/ic_messages_like_selected"
app:liked="true"
app:middlePadding="5dp"
app:rightPadding="5dp"
app:shiningSrc="@mipmap/ic_messages_like_selected_shining"
app:unlikeSrc="@mipmap/ic_messages_like_unselected" />
</LinearLayout>
<Button
android:id="@+id/animateBt"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:layout_margin="8dp"
android:text="anim"
android:textSize="16sp" />
</com.keyboard3.hencoderProduct.like.LikeLayout>
================================================
FILE: app/src/main/res/layout/move_layout.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<com.keyboard3.hencoderProduct.movement.MoveLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.keyboard3.hencoderProduct.movement.MoveView
android:id="@+id/objectAnimatorView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:background="@mipmap/bg_mi"/>
<Button
android:id="@+id/animateBt"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_margin="8dp"
android:text="@string/animate"
android:textSize="16sp" />
</com.keyboard3.hencoderProduct.movement.MoveLayout>
================================================
FILE: app/src/main/res/layout/ruler_layout.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<com.keyboard3.hencoderProduct.ruler.RulerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<com.keyboard3.RulerNumberLayout
android:id="@+id/knl_bottom_head"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:kgTextSize="20sp"
app:scaleTextSize="50sp" />
<com.keyboard3.BooheeRuler
android:id="@+id/br_top_head"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentTop="true"
app:bigScaleLength="40dp"
app:bigScaleWidth="2.5dp"
app:count="10"
app:currentScale="666666"
app:cursorDrawable="@drawable/cursor_shape"
app:cursorHeight="45dp"
app:cursorWidth="4dp"
app:maxScale="2000000"
app:minScale="464"
app:numberTextSize="22sp"
app:paddingStartAndEnd="10dp"
app:rulerStyle="top"
app:scaleInterval="11.5dp"
app:smallScaleLength="20dp"
app:smallScaleWidth="1.5dp"
app:targetRulerNumber="@id/knl_bottom_head"
app:textMarginHead="70dp" />
<com.keyboard3.BooheeRuler
android:id="@+id/br_bottom_head"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentTop="true"
app:bigScaleLength="40dp"
app:bigScaleWidth="2.5dp"
app:count="10"
app:currentScale="3456"
app:cursorDrawable="@drawable/cursor_shape"
app:cursorHeight="45dp"
app:cursorWidth="4dp"
app:maxScale="2000000"
app:minScale="464"
app:numberTextSize="22sp"
app:paddingStartAndEnd="10dp"
app:rulerStyle="bottom"
app:scaleInterval="11.5dp"
app:smallScaleLength="20dp"
app:smallScaleWidth="1.5dp"
app:targetRulerNumber="@id/knl_bottom_head"
app:textMarginHead="70dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:padding="10dp">
<com.keyboard3.BooheeRuler
android:id="@+id/br_left_head"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
app:bigScaleLength="40dp"
app:bigScaleWidth="2.5dp"
app:count="10"
app:currentScale="23"
app:cursorDrawable="@drawable/cursor_shape"
app:cursorHeight="45dp"
app:cursorWidth="4dp"
app:maxScale="2000000"
app:minScale="464"
app:numberTextSize="22sp"
app:paddingStartAndEnd="10dp"
app:rulerStyle="left"
app:scaleInterval="11.5dp"
app:smallScaleLength="20dp"
app:smallScaleWidth="1.5dp"
app:targetRulerNumber="@id/knl_bottom_head"
app:textMarginHead="70dp" />
<com.keyboard3.RulerNumberLayout
android:id="@+id/knl_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
app:kgTextSize="20sp"
app:kgUnitText="km"
app:scaleTextSize="50sp"
app:targetRuler="@id/br_bottom_head" />
<com.keyboard3.BooheeRuler
android:id="@+id/br_right_head"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
app:bigScaleLength="40dp"
app:bigScaleWidth="2.5dp"
app:count="10"
app:currentScale="99"
app:cursorDrawable="@drawable/cursor_shape"
app:cursorHeight="45dp"
app:cursorWidth="4dp"
app:maxScale="2000000"
app:minScale="464"
app:numberTextSize="22sp"
app:paddingStartAndEnd="10dp"
app:rulerStyle="right"
app:scaleInterval="11.5dp"
app:smallScaleLength="20dp"
app:smallScaleWidth="1.5dp"
app:targetRulerNumber="@id/knl_bottom_head"
app:textMarginHead="70dp" />
</LinearLayout>
</LinearLayout>
</com.keyboard3.hencoderProduct.ruler.RulerLayout>
================================================
FILE: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
================================================
FILE: app/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">HenCoder参赛作品by keyboard3</string>
<string name="title_flipboard">Flipboard翻页</string>
<string name="title_like">即刻点赞</string>
<string name="title_ruler">薄荷健康尺</string>
<string name="animate">animate</string>
<string name="title_move">小米运动</string>
</resources>
================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>
<declare-styleable name="LikeView">
<attr name="likeNum" format="integer"></attr>
<attr name="leftPadding" format="dimension"></attr>
<attr name="rightPadding" format="dimension"></attr>
<attr name="middlePadding" format="dimension"></attr>
<attr name="liked" format="boolean"></attr>
<attr name="likeSrc" format="reference"></attr>
<attr name="unlikeSrc" format="reference"></attr>
<attr name="shiningSrc" format="reference"></attr>
</declare-styleable>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="HenCoderTabTextAppearance">
<item name="textAllCaps">false</item>
<item name="android:textAllCaps">false</item>
</style>
</resources>
================================================
FILE: app/src/test/java/com/keyboard3/hencoderProduct/ExampleUnitTest.java
================================================
package com.keyboard3.hencoderProduct;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.2.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Oct 13 16:17:40 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -relativeX "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -relativeX "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "relativeX%~1" == "relativeX" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: ruler/.gitignore
================================================
/build
================================================
FILE: ruler/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
buildToolsVersion '26.0.2'
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:25.3.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
================================================
FILE: ruler/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: ruler/src/androidTest/java/com/keyboard3/ExampleInstrumentedTest.java
================================================
package com.keyboard3;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.keyboard3.test", appContext.getPackageName());
}
}
================================================
FILE: ruler/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.keyboard3" />
================================================
FILE: ruler/src/main/java/com/keyboard3/BooheeRuler.java
================================================
package com.keyboard3;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.IdRes;
import android.support.annotation.IntDef;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* 用于包着尺子的外壳,用于画选取光标、外壳
*/
public class BooheeRuler extends ViewGroup {
private final String TAG = "ruler";
private Context mContext;
//尺子Style定义
public static final int TOP_LAYOUT = 1, BOTTOM_LAYOUT = 3, LEFT_LAYOUT = 0, RIGHT_LAYOUT = 2;
@IntDef({TOP_LAYOUT, BOTTOM_LAYOUT, LEFT_LAYOUT, RIGHT_LAYOUT})
@Retention(RetentionPolicy.SOURCE)
public @interface RulerStyle {
}
private @BooheeRuler.RulerStyle
int mStyle = TOP_LAYOUT;
//内部的尺子
private InnerRuler mInnerRuler;
//最小最大刻度值(以0.1kg为单位)
private int mMinScale = 464, mMaxScale = 2000;
//光标宽度、高度
private int mCursorWidth = 8, mCursorHeight = 70;
//大小刻度的长度
private int mSmallScaleLength = 30, mBigScaleLength = 60;
//大小刻度的粗细
private int mSmallScaleWidth = 3, mBigScaleWidth = 5;
//数字字体大小
private int mTextSize = 28;
//数字Text距离顶部高度
private int mTextMarginHead = 120;
//刻度间隔
private int mInterval = 18;
private @IdRes
int mTargetRulerNumer;
//数字Text颜色
private
@ColorInt
int mTextColor = getResources().getColor(R.color.colorLightBlack);
//刻度颜色
private
@ColorInt
int mScaleColor = getResources().getColor(R.color.colorGray);
//初始的当前刻度
private float mCurrentScale = 0;
/**
* 一格大刻度多少格小刻度
*/
private int mCount = 10;
/**
* 光标drawable
*/
private Drawable mCursorDrawable;
/**
* 尺子两端的padding
*/
private int mPaddingHeadAndEnd = 0;
private int mPaddingLeft = 0, mPaddingTop = 0, mPaddingRight = 0, mPaddingBottom = 0;
/**
* 尺子背景
*/
private Drawable mRulerBackGround;
private int mRulerBackGroundColor = getResources().getColor(R.color.colorDirtyWithe);
/**
* 是否启用边缘效应
*/
private boolean mCanEdgeEffect = true;
/**
* 边缘颜色
*/
private @ColorInt
int mEdgeColor = getResources().getColor(R.color.colorForgiven);
public BooheeRuler(Context context) {
super(context);
initRuler(context);
}
public BooheeRuler(Context context, AttributeSet attrs) {
super(context, attrs);
initAttrs(context, attrs);
initRuler(context);
}
public BooheeRuler(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs);
initRuler(context);
}
@SuppressWarnings("WrongConstant")
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.BooheeRuler, 0, 0);
mMinScale = typedArray.getInteger(R.styleable.BooheeRuler_minScale, mMinScale);
mMaxScale = typedArray.getInteger(R.styleable.BooheeRuler_maxScale, mMaxScale);
mCursorWidth = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_cursorWidth, mCursorWidth);
mCursorHeight = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_cursorHeight, mCursorHeight);
mSmallScaleWidth = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_smallScaleWidth, mSmallScaleWidth);
mSmallScaleLength = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_smallScaleLength, mSmallScaleLength);
mBigScaleWidth = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_bigScaleWidth, mBigScaleWidth);
mBigScaleLength = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_bigScaleLength, mBigScaleLength);
mTextSize = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_numberTextSize, mTextSize);
mTextMarginHead = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_textMarginHead, mTextMarginHead);
mInterval = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_scaleInterval, mInterval);
mTextColor = typedArray.getColor(R.styleable.BooheeRuler_numberTextColor, mTextColor);
mScaleColor = typedArray.getColor(R.styleable.BooheeRuler_scaleColor, mScaleColor);
mCurrentScale = typedArray.getFloat(R.styleable.BooheeRuler_currentScale, (mMaxScale + mMinScale) / 2);
mCount = typedArray.getInt(R.styleable.BooheeRuler_count, mCount);
mCursorDrawable = typedArray.getDrawable(R.styleable.BooheeRuler_cursorDrawable);
mTargetRulerNumer = typedArray.getResourceId(R.styleable.BooheeRuler_targetRulerNumber, 0);
if (mCursorDrawable == null) {
mCursorDrawable = getResources().getDrawable(R.drawable.cursor_shape);
}
mPaddingHeadAndEnd = typedArray.getDimensionPixelSize(R.styleable.BooheeRuler_paddingStartAndEnd, mPaddingHeadAndEnd);
mStyle = typedArray.getInt(R.styleable.BooheeRuler_rulerStyle, mStyle);
mRulerBackGround = typedArray.getDrawable(R.styleable.BooheeRuler_rulerBackGround);
if (mRulerBackGround == null) {
mRulerBackGroundColor = typedArray.getColor(R.styleable.BooheeRuler_rulerBackGround, mRulerBackGroundColor);
}
mCanEdgeEffect = typedArray.getBoolean(R.styleable.BooheeRuler_canEdgeEffect, mCanEdgeEffect);
mEdgeColor = typedArray.getColor(R.styleable.BooheeRuler_edgeColor, mEdgeColor);
typedArray.recycle();
}
private void initRuler(Context context) {
mContext = context;
mInnerRuler = new InnerRuler(context, this, mStyle);
paddingHeadAndEnd();
//设置全屏,加入InnerRuler
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mInnerRuler.setLayoutParams(layoutParams);
addView(mInnerRuler);
//设置ViewGroup可画
setWillNotDraw(false);
initDrawable();
initRulerBackground();
}
private void initRulerBackground() {
if (mRulerBackGround != null) {
mInnerRuler.setBackground(mRulerBackGround);
} else {
mInnerRuler.setBackgroundColor(mRulerBackGroundColor);
}
}
/**
* 在宽高初始化之后定义光标Drawable的边界
*/
private void initDrawable() {
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
switch (mStyle) {
case TOP_LAYOUT:
mCursorDrawable.setBounds((getWidth() - mCursorWidth) / 2, 0
, (getWidth() + mCursorWidth) / 2, mCursorHeight);
break;
case BOTTOM_LAYOUT:
mCursorDrawable.setBounds((getWidth() - mCursorWidth) / 2, getHeight() - mCursorHeight
, (getWidth() + mCursorWidth) / 2, getHeight());
break;
case LEFT_LAYOUT:
mCursorDrawable.setBounds(0, (getHeight() - mCursorWidth) / 2
, mCursorHeight, (getHeight() + mCursorWidth) / 2);
break;
case RIGHT_LAYOUT:
mCursorDrawable.setBounds(getWidth() - mCursorHeight, (getHeight() - mCursorWidth) / 2
, getWidth(), (getHeight() + mCursorWidth) / 2);
break;
default:
}
return false;
}
});
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
//画中间的选定光标,要在这里画,因为dispatchDraw()执行在onDraw()后面,这样子光标才能不被尺子的刻度遮蔽
mCursorDrawable.draw(canvas);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
mInnerRuler.layout(mPaddingLeft, mPaddingTop, r - l - mPaddingRight, b - t - mPaddingBottom);
}
}
private void paddingHeadAndEnd() {
mPaddingBottom = mPaddingTop = InnerRuler.isVerticalRuler(mStyle) ? mPaddingHeadAndEnd : 0;
mPaddingRight = mPaddingLeft = InnerRuler.isVerticalRuler(mStyle) ? 0 : mPaddingHeadAndEnd;
}
/**
* 设置回调
*
* @param rulerCallback
*/
public void addCallback(RulerCallback rulerCallback) {
mInnerRuler.addRulerCallback(rulerCallback);
}
/**
* 设置当前进度
*
* @param currentScale
*/
public void setCurrentScale(float currentScale) {
mCurrentScale = currentScale;
mInnerRuler.setCurrentScale(currentScale);
}
/**
* 如果控件尺寸变化,中间光标的位置也要重新定义
*
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
initDrawable();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mTargetRulerNumer != 0) {
View view = getRootView().findViewById(mTargetRulerNumer);
if (view instanceof RulerCallback) {
RulerCallback callback = (RulerCallback) view;
addCallback(callback);
}
}
}
public int getEdgeColor() {
return mEdgeColor;
}
/**
* 设置能否使用边缘效果
*
* @param canEdgeEffect
*/
public void setCanEdgeEffect(boolean canEdgeEffect) {
this.mCanEdgeEffect = canEdgeEffect;
mInnerRuler.initEdgeEffects();
}
public boolean canEdgeEffect() {
return mCanEdgeEffect;
}
public float getCurrentScale() {
return mCurrentScale;
}
public void setMinScale(int minScale) {
this.mMinScale = minScale;
}
public int getMinScale() {
return mMinScale;
}
public void setMaxScale(int maxScale) {
this.mMaxScale = maxScale;
}
public int getMaxScale() {
return mMaxScale;
}
public void setCursorWidth(int cursorWidth) {
this.mCursorWidth = cursorWidth;
}
public int getCursorWidth() {
return mCursorWidth;
}
public void setCursorHeight(int cursorHeight) {
this.mCursorHeight = cursorHeight;
}
public int getCursorHeight() {
return mCursorHeight;
}
public void setBigScaleLength(int bigScaleLength) {
this.mBigScaleLength = bigScaleLength;
}
public int getBigScaleLength() {
return mBigScaleLength;
}
public void setBigScaleWidth(int bigScaleWidth) {
this.mBigScaleWidth = bigScaleWidth;
}
public int getBigScaleWidth() {
return mBigScaleWidth;
}
public void setSmallScaleLength(int smallScaleLength) {
this.mSmallScaleLength = smallScaleLength;
}
public int getSmallScaleLength() {
return mSmallScaleLength;
}
public void setSmallScaleWidth(int smallScaleWidth) {
this.mSmallScaleWidth = smallScaleWidth;
}
public int getSmallScaleWidth() {
return mSmallScaleWidth;
}
public void setTextMarginTop(int textMarginTop) {
this.mTextMarginHead = textMarginTop;
}
public int getTextMarginHead() {
return mTextMarginHead;
}
public void setTextSize(int textSize) {
this.mTextSize = textSize;
}
public int getTextSize() {
return mTextSize;
}
public void setInterval(int interval) {
this.mInterval = interval;
}
public int getInterval() {
return mInterval;
}
public int getTextColor() {
return mTextColor;
}
public int getScaleColor() {
return mScaleColor;
}
public void setCount(int mCount) {
this.mCount = mCount;
}
public int getCount() {
return mCount;
}
}
================================================
FILE: ruler/src/main/java/com/keyboard3/InnerRuler.java
================================================
package com.keyboard3;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.os.Build;
import android.support.annotation.Px;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.widget.EdgeEffect;
import android.widget.OverScroller;
import java.util.ArrayList;
import java.util.List;
/**
* 内部尺子抽象类
*/
public class InnerRuler extends View {
protected Context mContext;
protected BooheeRuler mParent;
protected Paint mSmallScalePaint, mBigScalePaint, mTextPaint, mOutLinePaint;
/**
* 当前刻度值
*/
protected float mCurrentScale = 0;
/**
* 最大刻度数
*/
protected int mMaxLength = 0;
/**
* 长度、最小可滑动值、最大可滑动值
*/
protected int mLength, mMinPosition = 0, mMaxPosition = 0;
/**
* 控制滑动
*/
protected OverScroller mOverScroller;
/**
* 一格大刻度多少格小刻度
*/
protected int mCount = 10;
/**
* 提前刻画量
*/
protected int mDrawOffset;
/**
* 速度获取
*/
protected VelocityTracker mVelocityTracker;
/**
* 惯性最大最小速度
*/
protected int mMaximumVelocity, mMinimumVelocity;
/**
* 回调接口
*/
protected List<RulerCallback> mRulerCallbacks = new ArrayList<>();
/**
* 边界效果
*/
protected EdgeEffect mStartEdgeEffect, mEndEdgeEffect;
/**
* 边缘效应长度
*/
protected int mEdgeLength;
int flag;
private float mLastY;
private float mLastX;
public InnerRuler(Context context, BooheeRuler booheeRuler, int flag) {
super(context);
mParent = booheeRuler;
this.flag = flag;
init(context);
}
private void init(Context context) {
mContext = context;
mMaxLength = mParent.getMaxScale() - mParent.getMinScale();
mCurrentScale = mParent.getCurrentScale();
mCount = mParent.getCount();
mDrawOffset = mCount * mParent.getInterval() / 2;
initPaints();
mOverScroller = new OverScroller(mContext);
//配置速度
mVelocityTracker = VelocityTracker.obtain();
mMaximumVelocity = ViewConfiguration.get(context)
.getScaledMaximumFlingVelocity();
mMinimumVelocity = ViewConfiguration.get(context)
.getScaledMinimumFlingVelocity();
initEdgeEffects();
//第一次进入,跳转到设定刻度
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
getViewTreeObserver().removeOnGlobalLayoutListener(this);
scroll2Scale(mCurrentScale);
}
});
checkAPILevel();
}
private void initPaints() {
mSmallScalePaint = new Paint();
mSmallScalePaint.setStrokeWidth(mParent.getSmallScaleWidth());
mSmallScalePaint.setColor(mParent.getScaleColor());
mSmallScalePaint.setStrokeCap(Paint.Cap.ROUND);
mBigScalePaint = new Paint();
mBigScalePaint.setColor(mParent.getScaleColor());
mBigScalePaint.setStrokeWidth(mParent.getBigScaleWidth());
mBigScalePaint.setStrokeCap(Paint.Cap.ROUND);
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(mParent.getTextColor());
mTextPaint.setTextSize(mParent.getTextSize());
mTextPaint.setTextAlign(Paint.Align.CENTER);
mOutLinePaint = new Paint();
mOutLinePaint.setStrokeWidth(0);
mOutLinePaint.setColor(mParent.getScaleColor());
}
/**
* 初始化边缘效果
*/
public void initEdgeEffects() {
if (mParent.canEdgeEffect()) {
if (mStartEdgeEffect == null || mEndEdgeEffect == null) {
mStartEdgeEffect = new EdgeEffect(mContext);
mEndEdgeEffect = new EdgeEffect(mContext);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mStartEdgeEffect.setColor(mParent.getEdgeColor());
mEndEdgeEffect.setColor(mParent.getEdgeColor());
}
mEdgeLength = mParent.getCursorHeight() + mParent.getInterval() * mParent.getCount();
}
}
}
private void checkAPILevel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
setLayerType(LAYER_TYPE_NONE, null);
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
this.mRulerCallbacks = null;
}
/**
* 设置尺子当前刻度
*
* @param currentScale
*/
public void setCurrentScale(float currentScale) {
this.mCurrentScale = currentScale;
scroll2Scale(mCurrentScale);
}
public void addRulerCallback(RulerCallback RulerCallback) {
mRulerCallbacks.add(RulerCallback);
}
public float getCurrentScale() {
return mCurrentScale;
}
//======绘制逻辑=======
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawRulerBody(canvas, flag);
}
protected void drawRulerBody(Canvas canvas, int flag) {
float location = 0, length;
if (isVerticalRuler(flag)) {
//刻度是水平的 Y是固定的
location = getScrollY();
length = canvas.getHeight();
} else {
//刻度是竖直的 x是固定的
location = getScrollX();
length = canvas.getWidth();
}
int start = (int) ((location - mDrawOffset) / mParent.getInterval() + mParent.getMinScale());
int end = (int) ((location + length + mDrawOffset) / mParent.getInterval() + mParent.getMinScale());
for (int i = start; i <= end; i++) {
if (i >= mParent.getMinScale() && i <= mParent.getMaxScale()) {
if (i % mCount == 0) {
PointF[] line = getLine(mParent, flag, mParent.getBigScaleLength(), i, canvas.getHeight(), canvas.getWidth());
canvas.drawLine(line[0].x, line[0].y, line[1].x, line[1].y, mBigScalePaint);
PointF textEndPoint = getTextPoint(mParent, flag, i, canvas.getHeight(), canvas.getWidth());
canvas.drawText(String.valueOf(i / 10), textEndPoint.x, textEndPoint.y, mTextPaint);
} else {
PointF[] line = getLine(mParent, flag, mParent.getSmallScaleLength(), i, canvas.getHeight(), canvas.getWidth());
canvas.drawLine(line[0].x, line[0].y, line[1].x, line[1].y, mSmallScalePaint);
}
}
}
//画轮廓线
PointF[] outLine = getOutLine(this, flag, canvas.getHeight(), canvas.getWidth());
canvas.drawLine(outLine[0].x, outLine[0].y, outLine[1].x, outLine[1].y, mOutLinePaint);
PointF[] outLine2 = getOutLine(this, getNegativeFlag(flag), canvas.getHeight(), canvas.getWidth());
canvas.drawLine(outLine2[0].x, outLine2[0].y, outLine2[1].x, outLine2[1].y, mOutLinePaint);
PointF[] outLine3 = getOutLeftOrRightLine(this, flag, true, canvas.getHeight(), canvas.getWidth());
canvas.drawLine(outLine3[0].x, outLine3[0].y, outLine3[1].x, outLine3[1].y, mOutLinePaint);
PointF[] outLine4 = getOutLeftOrRightLine(this, flag, false, canvas.getHeight(), canvas.getWidth());
canvas.drawLine(outLine4[0].x, outLine4[0].y, outLine4[1].x, outLine4[1].y, mOutLinePaint);
}
protected static boolean isVerticalRuler(int flag) {
return flag == 0 || flag == 2;
}
protected static int getNegativeFlag(int flag) {
return (flag + 2) % 4;
}
private static PointF getTextPoint(BooheeRuler mParent, int flag, float location, int height, int width) {
PointF pointF = new PointF();
float locationY = 0, locationX = 0;
if (isVerticalRuler(flag)) {
//刻度是水平的 Y是固定的
locationY = (location - mParent.getMinScale()) * mParent.getInterval();
} else {
//刻度是竖直的 x是固定的
locationX = (location - mParent.getMinScale()) * mParent.getInterval();
}
switch (flag) {
case BooheeRuler.LEFT_LAYOUT:
pointF.x = mParent.getTextMarginHead();
pointF.y = locationY + mParent.getTextSize() / 2;
break;
case BooheeRuler.TOP_LAYOUT:
pointF.x = locationX;
pointF.y = mParent.getTextMarginHead();
break;
case BooheeRuler.RIGHT_LAYOUT:
pointF.x = width - mParent.getTextMarginHead();
pointF.y = locationY + mParent.getTextSize() / 2;
break;
case BooheeRuler.BOTTOM_LAYOUT:
pointF.x = locationX;
pointF.y = height - mParent.getTextMarginHead();
break;
default:
}
return pointF;
}
private static PointF[] getLine(BooheeRuler mParent, int flag, int length, float location, int height, int width) {
PointF startPoint = new PointF();
PointF stopPoint = new PointF();
float locationY = 0, locationX = 0;
if (isVerticalRuler(flag)) {
//刻度是水平的 Y是固定的
locationY = (location - mParent.getMinScale()) * mParent.getInterval();
} else {//水平的尺子
//刻度是竖直的 x是固定的
locationX = (location - mParent.getMinScale()) * mParent.getInterval();
}
switch (flag) {
case BooheeRuler.LEFT_LAYOUT:
startPoint.y = locationY;
stopPoint.x = length;
stopPoint.y = locationY;
break;
case BooheeRuler.TOP_LAYOUT:
startPoint.x = locationX;
stopPoint.x = locationX;
stopPoint.y = length;
break;
case BooheeRuler.RIGHT_LAYOUT:
startPoint.x = width - length;
startPoint.y = locationY;
stopPoint.x = width;
stopPoint.y = locationY;
break;
case BooheeRuler.BOTTOM_LAYOUT:
startPoint.x = locationX;
startPoint.y = height - length;
stopPoint.x = locationX;
stopPoint.y = height;
break;
default:
}
return new PointF[]{startPoint, stopPoint};
}
private static PointF[] getOutLine(View contentView, int flag, int height, int width) {
PointF startPoint = new PointF();
PointF stopPoint = new PointF();
float locationY = 0, locationX = 0;
if (isVerticalRuler(flag)) {
//边界线是竖直的 竖直滚动的起始位置
locationY = contentView.getScrollY();
} else {
//边界线是水平的 水平滚动的起始位置
locationX = contentView.getScrollX();
}
switch (flag) {
case BooheeRuler.LEFT_LAYOUT:
startPoint.y = locationY;
stopPoint.y = locationY + height;
break;
case BooheeRuler.TOP_LAYOUT:
startPoint.x = locationX;
stopPoint.x = locationX + width;
break;
case BooheeRuler.RIGHT_LAYOUT:
startPoint.y = locationY;
stopPoint.y = locationY + height;
startPoint.x = width - 0.1F;
stopPoint.x = width - 0.1F;
break;
case BooheeRuler.BOTTOM_LAYOUT:
startPoint.x = locationX;
startPoint.y = height - 0.1F;
stopPoint.x = locationX + width;
stopPoint.y = height - 0.1F;
break;
default:
}
return new PointF[]{startPoint, stopPoint};
}
private static PointF[] getOutLeftOrRightLine(View contentView, int flag, boolean isleft, int height, int width) {
PointF startPoint = new PointF();
PointF stopPoint = new PointF();
float locationY = 0, locationX = 0;
if (isVerticalRuler(flag)) {
//边界线是竖直的 竖直滚动的起始位置
locationY = contentView.getScrollY();
} else {
//边界线是水平的 水平滚动的起始位置
locationX = contentView.getScrollX();
}
switch (flag) {
case BooheeRuler.LEFT_LAYOUT:
if (isleft) {
startPoint.y = locationY;
stopPoint.y = locationY;
stopPoint.x = width;
} else {
startPoint.y = locationY + height - 0.1F;
stopPoint.y = locationY + height - 0.1F;
stopPoint.x = width;
}
break;
case BooheeRuler.TOP_LAYOUT:
if (isleft) {
startPoint.x = locationX;
stopPoint.x = locationX;
stopPoint.y = height;
} else {
startPoint.x = locationX + width - 0.1F;
stopPoint.x = locationX + width - 0.1F;
stopPoint.y = height;
}
break;
case BooheeRuler.RIGHT_LAYOUT:
if (isleft) {
startPoint.y = locationY;
stopPoint.y = locationY;
startPoint.x = width - 0.1F;
} else {
startPoint.y = locationY + height - 0.1F;
stopPoint.y = locationY + height - 0.1F;
startPoint.x = width - 0.1F;
}
break;
case BooheeRuler.BOTTOM_LAYOUT:
if (isleft) {
startPoint.x = locationX;
startPoint.y = height - 0.1F;
stopPoint.x = locationX;
} else {
startPoint.x = locationX + width - 0.1F;
startPoint.y = height - 0.1F;
stopPoint.x = locationX + width - 0.1F;
}
break;
default:
}
return new PointF[]{startPoint, stopPoint};
}
//======滑动事件逻辑======
//获取控件宽高,设置相应信息
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mLength = (mParent.getMaxScale() - mParent.getMinScale()) * mParent.getInterval();
int mHalf = (isVerticalRuler(flag) ? h : w) / 2;
mMinPosition = -mHalf;
mMaxPosition = mLength - mHalf;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float currentY = event.getY();
float currentX = event.getX();
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mOverScroller.isFinished()) {
mOverScroller.abortAnimation();
}
mLastY = currentY;
mLastX = currentX;
break;
case MotionEvent.ACTION_MOVE:
float moveX = isVerticalRuler(flag) ? 0 : mLastX - currentX;
float moveY = isVerticalRuler(flag) ? mLastY - currentY : 0;
scrollBy((int) moveX, (int) moveY);
mLastY = currentY;
mLastX = currentX;
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int velocityY = 0, velocityX = 0;
boolean startScroll = false;
if (isVerticalRuler(flag)) {
velocityY = (int) mVelocityTracker.getYVelocity();
startScroll = Math.abs(velocityY) > mMinimumVelocity;
} else {
velocityX = (int) mVelocityTracker.getXVelocity();
startScroll = Math.abs(velocityX) > mMinimumVelocity;
}
if (startScroll) {
fling(isVerticalRuler(flag), -velocityX, -velocityY);
} else {
scrollBackToCurrentScale(isVerticalRuler(flag));
}
//VelocityTracker回收
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
//releaseEdgeEffects();
break;
case MotionEvent.ACTION_CANCEL:
if (!mOverScroller.isFinished()) {
mOverScroller.abortAnimation();
}
scrollBackToCurrentScale(isVerticalRuler(flag));
//VelocityTracker回收
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
//releaseEdgeEffects();
break;
default:
}
return true;
}
private void fling(boolean isVertical, int vx, int vy) {
int startX = 0, startY = 0, velocityX = 0, velocityY = 0, minX = 0, minY = 0, maxX = 0, maxY = 0;
startX = isVertical ? 0 : getScrollX();
startY = isVertical ? getScrollY() : 0;
velocityX = isVertical ? 0 : vx;
velocityY = isVertical ? vy : 0;
int min = mMinPosition - mEdgeLength;
int max = mMaxPosition + mEdgeLength;
minX = isVertical ? 0 : min;
minY = isVertical ? 0 : max;
maxX = isVertical ? min : 0;
maxY = isVertical ? max : 0;
mOverScroller.fling(startX, startY, velocityX, velocityY, minX, minY, maxX, maxY);
invalidate();
}
@Override
public void computeScroll() {
if (mOverScroller.computeScrollOffset()) {
scrollTo(mOverScroller.getCurrX(), mOverScroller.getCurrY());
if (!mOverScroller.computeScrollOffset() && mCurrentScale != Math.round(mCurrentScale)) {
scrollBackToCurrentScale(isVerticalRuler(flag));
}
invalidate();
}
}
protected void scrollBackToCurrentScale(boolean isVertical) {
int startX = 0, startY = 0, destX, destY = 0;
startX = isVertical ? 0 : getScrollX();
startY = isVertical ? getScrollY() : 0;
destX = isVertical ? 0 : scale2ScrollXY(Math.round(mCurrentScale)) - startX;
destY = isVertical ? scale2ScrollXY(Math.round(mCurrentScale)) - startY : 0;
mOverScroller.startScroll(startX, startY, destX, destY, 1000);
invalidate();
}
protected void scroll2Scale(float scale) {
mCurrentScale = Math.round(scale);
scrollTo(0, scale2ScrollXY(mCurrentScale));
}
/**
* 将移动过程中经过的刻度显示出来
*
* @param x
* @param y
*/
@Override
public void scrollTo(@Px int x, @Px int y) {
if (isVerticalRuler(flag)) {
if (y < mMinPosition || y > mMaxPosition) {
y = mMinPosition;
}
if (y != getScrollY()) {
super.scrollTo(x, y);
}
mCurrentScale = scrollXY2Scale(y);
} else {
if (x < mMinPosition || x > mMaxPosition) {
x = mMinPosition;
}
if (x != getScrollX()) {
super.scrollTo(x, y);
}
mCurrentScale = scrollXY2Scale(x);
}
if (mRulerCallbacks != null) {
for (RulerCallback item :
mRulerCallbacks) {
item.onScaleChanging(Math.round(mCurrentScale));
}
}
}
/**
* 将滚动位置转化为刻度
*
* @param scrollXY
* @return
*/
private float scrollXY2Scale(int scrollXY) {
return ((float) (scrollXY - mMinPosition) / mLength) * mMaxLength + mParent.getMinScale();
}
/**
* 将刻度转化为移动的位置
*
* @param scale
* @return
*/
private int scale2ScrollXY(float scale) {
return (int) ((scale - mParent.getMinScale()) / mMaxLength * mLength + mMinPosition);
}
}
================================================
FILE: ruler/src/main/java/com/keyboard3/RulerCallback.java
================================================
package com.keyboard3;
/**
* Created by yany on 2017/10/16.
*/
public interface RulerCallback {
//选取刻度变化的时候回调
void onScaleChanging(float scale);
//选取刻度变化完成的时候回调
// void afterScaleChanged(float scale);
}
================================================
FILE: ruler/src/main/java/com/keyboard3/RulerNumberLayout.java
================================================
package com.keyboard3;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.ColorInt;
import android.support.annotation.IdRes;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* Created by yany on 2017/10/17.
* 用于包着显示具体数字刻度Layout
*/
public class RulerNumberLayout extends RelativeLayout implements RulerCallback {
private TextView tv_scale, tv_kg;
//字体大小
private float mScaleTextSize = 80, mKgTextSize = 40;
//字体颜色
private @ColorInt
int mScaleTextColor = getResources().getColor(R.color.colorForgiven);
private @ColorInt
int mKgTextColor = getResources().getColor(R.color.colorForgiven);
@IdRes
int mTargetRuler;
//kg单位文字
private String mUnitText = "kg";
public RulerNumberLayout(Context context) {
super(context);
init(context);
}
public RulerNumberLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initAttrs(context, attrs);
init(context);
}
public RulerNumberLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RulerNumberLayout, 0, 0);
mScaleTextSize = typedArray.getDimension(R.styleable.RulerNumberLayout_scaleTextSize, mScaleTextSize);
mKgTextSize = typedArray.getDimension(R.styleable.RulerNumberLayout_kgTextSize, mKgTextSize);
mScaleTextColor = typedArray.getColor(R.styleable.RulerNumberLayout_scaleTextColor, mScaleTextColor);
mKgTextColor = typedArray.getColor(R.styleable.RulerNumberLayout_kgTextColor, mKgTextColor);
mKgTextColor = typedArray.getColor(R.styleable.RulerNumberLayout_kgTextColor, mKgTextColor);
mTargetRuler = typedArray.getResourceId(R.styleable.RulerNumberLayout_targetRuler, 0);
String text = typedArray.getString(R.styleable.RulerNumberLayout_kgUnitText);
if (text != null) {
mUnitText = text;
}
typedArray.recycle();
}
private void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.layout_kg_number, this);
tv_scale = (TextView) findViewById(R.id.tv_scale);
tv_kg = (TextView) findViewById(R.id.tv_kg);
tv_scale.setTextSize(TypedValue.COMPLEX_UNIT_PX, mScaleTextSize);
tv_scale.setTextColor(mScaleTextColor);
tv_kg.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKgTextSize);
tv_kg.setTextColor(mKgTextColor);
tv_kg.setText(mUnitText);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mTargetRuler != 0) {
View ruler = getRootView().findViewById(mTargetRuler);
if (ruler instanceof BooheeRuler) {
((BooheeRuler) ruler).addCallback(this);
}
}
}
public void bindRuler(BooheeRuler booheeRuler) {
booheeRuler.addCallback(this);
}
@Override
public void onScaleChanging(float scale) {
tv_scale.setText(String.valueOf(scale / 10));
}
}
================================================
FILE: ruler/src/main/res/drawable/cursor_shape.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorForgiven" />
<corners android:radius="2dp"/>
</shape>
================================================
FILE: ruler/src/main/res/layout/layout_kg_number.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_scale"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<TextView
android:id="@+id/tv_kg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/tv_scale"
android:layout_marginLeft="6dp"
android:layout_toRightOf="@+id/tv_scale"
android:text="kg" />
</RelativeLayout>
================================================
FILE: ruler/src/main/res/values/attr.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="BooheeRuler">
<attr name="minScale" format="integer" />
<attr name="maxScale" format="integer" />
<attr name="smallScaleLength" format="dimension" />
<attr name="smallScaleWidth" format="dimension" />
<attr name="bigScaleLength" format="dimension" />
<attr name="bigScaleWidth" format="dimension" />
<attr name="cursorHeight" format="dimension" />
<attr name="cursorWidth" format="dimension" />
<attr name="textMarginHead" format="dimension" />
<attr name="numberTextSize" format="dimension" />
<attr name="scaleInterval" format="dimension" />
<attr name="numberTextColor" format="color" />
<attr name="scaleColor" format="color" />
<attr name="cursorDrawable" format="dimension" />
<attr name="targetRulerNumber" format="reference" />
<attr name="currentScale" format="float" />
<attr name="paddingStartAndEnd" format="dimension" />
<attr name="count" format="integer" />
<attr name="rulerStyle" format="enum">
<enum name="left" value="0" />
<enum name="top" value="1" />
<enum name="right" value="2" />
<enum name="bottom" value="3" />
</attr>
<attr name="rulerBackGround" format="reference|color" />
<attr name="canEdgeEffect" format="boolean" />
<attr name="edgeColor" format="color" />
</declare-styleable>
<declare-styleable name="RulerNumberLayout">
<attr name="scaleTextSize" format="dimension" />
<attr name="kgTextSize" format="dimension" />
<attr name="scaleTextColor" format="color" />
<attr name="kgTextColor" format="color" />
<attr name="kgUnitText" format="string" />
<attr name="targetRuler" format="reference" />
</declare-styleable>
</resources>
================================================
FILE: ruler/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorGray">#e2e5e2</color>
<color name="colorDirtyWithe">#f6f9f6</color>
<color name="colorForgiven">#4bbb74</color>
<color name="colorLightBlack">#2B2E2B</color>
</resources>
================================================
FILE: ruler/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">Ruler</string>
</resources>
================================================
FILE: ruler/src/test/java/com/keyboard3/ExampleUnitTest.java
================================================
package com.keyboard3;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: settings.gradle
================================================
include ':app', ':ruler'
gitextract_oi1ooax2/ ├── .gitignore ├── README.md ├── app/ │ ├── .gitignore │ ├── build/ │ │ └── outputs/ │ │ └── apk/ │ │ └── debug/ │ │ └── app-debug.apk │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── keyboard3/ │ │ └── hencoderProduct/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── keyboard3/ │ │ │ └── hencoderProduct/ │ │ │ ├── MainActivity.java │ │ │ ├── PageFragment.java │ │ │ ├── Utils.java │ │ │ ├── filpboard/ │ │ │ │ ├── FlipboardLayout.java │ │ │ │ └── FlipboardView.java │ │ │ ├── like/ │ │ │ │ ├── LikeImageView.java │ │ │ │ ├── LikeLayout.java │ │ │ │ ├── LikeNumView.java │ │ │ │ └── LikeView.java │ │ │ ├── movement/ │ │ │ │ ├── MoveLayout.java │ │ │ │ └── MoveView.java │ │ │ └── ruler/ │ │ │ └── RulerLayout.java │ │ └── res/ │ │ ├── layout/ │ │ │ ├── activity_main.xml │ │ │ ├── filpboard_layout.xml │ │ │ ├── fragment_page.xml │ │ │ ├── like_layout.xml │ │ │ ├── move_layout.xml │ │ │ └── ruler_layout.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── com/ │ └── keyboard3/ │ └── hencoderProduct/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── ruler/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── keyboard3/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── keyboard3/ │ │ │ ├── BooheeRuler.java │ │ │ ├── InnerRuler.java │ │ │ ├── RulerCallback.java │ │ │ └── RulerNumberLayout.java │ │ └── res/ │ │ ├── drawable/ │ │ │ └── cursor_shape.xml │ │ ├── layout/ │ │ │ └── layout_kg_number.xml │ │ └── values/ │ │ ├── attr.xml │ │ ├── colors.xml │ │ └── strings.xml │ └── test/ │ └── java/ │ └── com/ │ └── keyboard3/ │ └── ExampleUnitTest.java └── settings.gradle
SYMBOL INDEX (192 symbols across 20 files)
FILE: app/src/androidTest/java/com/keyboard3/hencoderProduct/ExampleInstrumentedTest.java
class ExampleInstrumentedTest (line 17) | @RunWith(AndroidJUnit4.class)
method useAppContext (line 19) | @Test
FILE: app/src/main/java/com/keyboard3/hencoderProduct/MainActivity.java
class MainActivity (line 19) | public class MainActivity extends AppCompatActivity {
method onCreate (line 31) | @Override
method onCreateOptionsMenu (line 60) | @Override
class PageModel (line 65) | private class PageModel {
method PageModel (line 71) | PageModel(@LayoutRes int sampleLayoutRes, @StringRes int titleRes) {
FILE: app/src/main/java/com/keyboard3/hencoderProduct/PageFragment.java
class PageFragment (line 13) | public class PageFragment extends Fragment {
method newInstance (line 17) | public static PageFragment newInstance(@LayoutRes int productLayoutRes) {
method onCreateView (line 25) | @Nullable
method onCreate (line 37) | @Override
FILE: app/src/main/java/com/keyboard3/hencoderProduct/Utils.java
class Utils (line 6) | public class Utils {
method dpToPixel (line 7) | public static float dpToPixel(float dp) {
FILE: app/src/main/java/com/keyboard3/hencoderProduct/filpboard/FlipboardLayout.java
class FlipboardLayout (line 17) | public class FlipboardLayout extends RelativeLayout {
method FlipboardLayout (line 21) | public FlipboardLayout(Context context) {
method FlipboardLayout (line 25) | public FlipboardLayout(Context context, AttributeSet attrs) {
method FlipboardLayout (line 29) | public FlipboardLayout(Context context, AttributeSet attrs, int defSty...
method draw (line 33) | @Override
method onAttachedToWindow (line 38) | @Override
FILE: app/src/main/java/com/keyboard3/hencoderProduct/filpboard/FlipboardView.java
class FlipboardView (line 19) | public class FlipboardView extends View {
method FlipboardView (line 27) | public FlipboardView(Context context) {
method FlipboardView (line 31) | public FlipboardView(Context context, @Nullable AttributeSet attrs) {
method FlipboardView (line 35) | public FlipboardView(Context context, @Nullable AttributeSet attrs, in...
method clearCanvas (line 43) | public void clearCanvas() {
method onAttachedToWindow (line 50) | @Override
method onDetachedFromWindow (line 55) | @Override
method onDraw (line 60) | @Override
method setDegree (line 109) | @SuppressWarnings("unused")
method setTurnoverDegreeLast (line 115) | @SuppressWarnings("unused")
method setTurnoverDegreeFirst (line 121) | @SuppressWarnings("unused")
FILE: app/src/main/java/com/keyboard3/hencoderProduct/like/LikeImageView.java
class LikeImageView (line 19) | public class LikeImageView extends View {
method LikeImageView (line 31) | public LikeImageView(Context context) {
method LikeImageView (line 35) | public LikeImageView(Context context, @Nullable AttributeSet attrs) {
method LikeImageView (line 39) | public LikeImageView(Context context, @Nullable AttributeSet attrs, in...
method onMeasure (line 47) | @Override
method setLike (line 55) | public void setLike(boolean isLike) {
method onDraw (line 63) | @Override
method drawCircle (line 76) | private void drawCircle(Canvas canvas, int centerX, int centerY) {
method drawLike (line 97) | private void drawLike(Canvas canvas, int likeTop, int likeLeft) {
method drawShining (line 143) | private void drawShining(Canvas canvas, int likeTop, int likeLeft) {
method setAnimProgress (line 157) | @SuppressWarnings("unused")
method setMiddlePadding (line 163) | public void setMiddlePadding(float middlePadding) {
method setLeftPadding (line 167) | public void setLeftPadding(float leftPadding) {
method setUnlikeSrc (line 171) | public void setUnlikeSrc(@IdRes int unlikeSrc) {
method setLikedSrc (line 175) | public void setLikedSrc(@IdRes int likeSrc) {
method setShiningdSrc (line 179) | public void setShiningdSrc(@IdRes int shiningSrc) {
FILE: app/src/main/java/com/keyboard3/hencoderProduct/like/LikeLayout.java
class LikeLayout (line 13) | public class LikeLayout extends RelativeLayout {
method LikeLayout (line 17) | public LikeLayout(Context context) {
method LikeLayout (line 21) | public LikeLayout(Context context, AttributeSet attrs) {
method LikeLayout (line 25) | public LikeLayout(Context context, AttributeSet attrs, int defStyleAtt...
method onAttachedToWindow (line 29) | @Override
FILE: app/src/main/java/com/keyboard3/hencoderProduct/like/LikeNumView.java
class LikeNumView (line 18) | public class LikeNumView extends View {
method LikeNumView (line 29) | public LikeNumView(Context context) {
method LikeNumView (line 33) | public LikeNumView(Context context, @Nullable AttributeSet attrs) {
method LikeNumView (line 37) | public LikeNumView(Context context, @Nullable AttributeSet attrs, int ...
method setNum (line 47) | protected void setNum(int num) {
method onAttachedToWindow (line 52) | @Override
method onMeasure (line 57) | @Override
method changeLike (line 71) | protected void changeLike(boolean isLike) {
method init (line 82) | protected void init() {
method onDraw (line 86) | @Override
method drawAnimNum (line 96) | private void drawAnimNum(Canvas canvas, int leftX, int baseTxtY, int c...
method optDrawNum (line 119) | private void optDrawNum(Canvas canvas, int leftX, int baseTxtY, String...
method setLiked (line 142) | public void setLiked(boolean liked) {
method setTranslationY (line 146) | @SuppressWarnings("unused")
method setRightPadding (line 152) | public void setRightPadding(float rightPadding) {
FILE: app/src/main/java/com/keyboard3/hencoderProduct/like/LikeView.java
class LikeView (line 20) | public class LikeView extends LinearLayout {
method LikeView (line 27) | public LikeView(Context context) {
method LikeView (line 31) | public LikeView(Context context, AttributeSet attrs) {
method LikeView (line 35) | public LikeView(Context context, AttributeSet attrs, int defStyleAttr) {
method onClick (line 64) | @Override
method setNum (line 76) | public void setNum(int num) {
method onDetachedFromWindow (line 80) | @Override
method onMeasure (line 86) | @Override
method setLike (line 110) | public void setLike(boolean like) {
FILE: app/src/main/java/com/keyboard3/hencoderProduct/movement/MoveLayout.java
class MoveLayout (line 14) | public class MoveLayout extends RelativeLayout {
method MoveLayout (line 18) | public MoveLayout(Context context) {
method MoveLayout (line 22) | public MoveLayout(Context context, AttributeSet attrs) {
method MoveLayout (line 26) | public MoveLayout(Context context, AttributeSet attrs, int defStyleAtt...
method onAttachedToWindow (line 30) | @Override
FILE: app/src/main/java/com/keyboard3/hencoderProduct/movement/MoveView.java
class MoveView (line 32) | public class MoveView extends View {
method MoveView (line 50) | public MoveView(Context context) {
method MoveView (line 54) | public MoveView(Context context, @Nullable AttributeSet attrs) {
method MoveView (line 58) | public MoveView(Context context, @Nullable AttributeSet attrs, int def...
method startAnimal (line 74) | public void startAnimal() {
method onAttachedToWindow (line 108) | @Override
method onDetachedFromWindow (line 113) | @Override
method onMeasure (line 119) | @Override
method onLayout (line 125) | @Override
method onDraw (line 130) | @Override
method getPercent (line 233) | private float getPercent() {
method setTranslationY (line 237) | @Override
method setDegree (line 243) | @SuppressWarnings("unused")
class AnimatorListener (line 249) | public abstract class AnimatorListener implements Animator.AnimatorLis...
method onAnimationStart (line 251) | @Override
method onAnimationCancel (line 256) | @Override
method onAnimationRepeat (line 261) | @Override
method onAnimationEnd (line 266) | @Override
FILE: app/src/main/java/com/keyboard3/hencoderProduct/ruler/RulerLayout.java
class RulerLayout (line 12) | public class RulerLayout extends RelativeLayout {
method RulerLayout (line 13) | public RulerLayout(Context context) {
method RulerLayout (line 17) | public RulerLayout(Context context, AttributeSet attrs) {
method RulerLayout (line 21) | public RulerLayout(Context context, AttributeSet attrs, int defStyleAt...
FILE: app/src/test/java/com/keyboard3/hencoderProduct/ExampleUnitTest.java
class ExampleUnitTest (line 12) | public class ExampleUnitTest {
method addition_isCorrect (line 13) | @Test
FILE: ruler/src/androidTest/java/com/keyboard3/ExampleInstrumentedTest.java
class ExampleInstrumentedTest (line 17) | @RunWith(AndroidJUnit4.class)
method useAppContext (line 19) | @Test
FILE: ruler/src/main/java/com/keyboard3/BooheeRuler.java
class BooheeRuler (line 22) | public class BooheeRuler extends ViewGroup {
method BooheeRuler (line 92) | public BooheeRuler(Context context) {
method BooheeRuler (line 98) | public BooheeRuler(Context context, AttributeSet attrs) {
method BooheeRuler (line 105) | public BooheeRuler(Context context, AttributeSet attrs, int defStyleAt...
method initAttrs (line 112) | @SuppressWarnings("WrongConstant")
method initRuler (line 146) | private void initRuler(Context context) {
method initRulerBackground (line 162) | private void initRulerBackground() {
method initDrawable (line 173) | private void initDrawable() {
method dispatchDraw (line 204) | @Override
method onLayout (line 211) | @Override
method paddingHeadAndEnd (line 218) | private void paddingHeadAndEnd() {
method addCallback (line 228) | public void addCallback(RulerCallback rulerCallback) {
method setCurrentScale (line 237) | public void setCurrentScale(float currentScale) {
method onSizeChanged (line 250) | @Override
method onAttachedToWindow (line 256) | @Override
method getEdgeColor (line 268) | public int getEdgeColor() {
method setCanEdgeEffect (line 277) | public void setCanEdgeEffect(boolean canEdgeEffect) {
method canEdgeEffect (line 282) | public boolean canEdgeEffect() {
method getCurrentScale (line 286) | public float getCurrentScale() {
method setMinScale (line 290) | public void setMinScale(int minScale) {
method getMinScale (line 294) | public int getMinScale() {
method setMaxScale (line 298) | public void setMaxScale(int maxScale) {
method getMaxScale (line 302) | public int getMaxScale() {
method setCursorWidth (line 306) | public void setCursorWidth(int cursorWidth) {
method getCursorWidth (line 310) | public int getCursorWidth() {
method setCursorHeight (line 314) | public void setCursorHeight(int cursorHeight) {
method getCursorHeight (line 318) | public int getCursorHeight() {
method setBigScaleLength (line 323) | public void setBigScaleLength(int bigScaleLength) {
method getBigScaleLength (line 327) | public int getBigScaleLength() {
method setBigScaleWidth (line 331) | public void setBigScaleWidth(int bigScaleWidth) {
method getBigScaleWidth (line 335) | public int getBigScaleWidth() {
method setSmallScaleLength (line 339) | public void setSmallScaleLength(int smallScaleLength) {
method getSmallScaleLength (line 343) | public int getSmallScaleLength() {
method setSmallScaleWidth (line 347) | public void setSmallScaleWidth(int smallScaleWidth) {
method getSmallScaleWidth (line 351) | public int getSmallScaleWidth() {
method setTextMarginTop (line 355) | public void setTextMarginTop(int textMarginTop) {
method getTextMarginHead (line 359) | public int getTextMarginHead() {
method setTextSize (line 363) | public void setTextSize(int textSize) {
method getTextSize (line 367) | public int getTextSize() {
method setInterval (line 371) | public void setInterval(int interval) {
method getInterval (line 375) | public int getInterval() {
method getTextColor (line 379) | public int getTextColor() {
method getScaleColor (line 383) | public int getScaleColor() {
method setCount (line 387) | public void setCount(int mCount) {
method getCount (line 391) | public int getCount() {
FILE: ruler/src/main/java/com/keyboard3/InnerRuler.java
class InnerRuler (line 24) | public class InnerRuler extends View {
method InnerRuler (line 77) | public InnerRuler(Context context, BooheeRuler booheeRuler, int flag) {
method init (line 84) | private void init(Context context) {
method initPaints (line 116) | private void initPaints() {
method initEdgeEffects (line 140) | public void initEdgeEffects() {
method checkAPILevel (line 155) | private void checkAPILevel() {
method onDetachedFromWindow (line 161) | @Override
method setCurrentScale (line 172) | public void setCurrentScale(float currentScale) {
method addRulerCallback (line 177) | public void addRulerCallback(RulerCallback RulerCallback) {
method getCurrentScale (line 181) | public float getCurrentScale() {
method onDraw (line 186) | @Override
method drawRulerBody (line 192) | protected void drawRulerBody(Canvas canvas, int flag) {
method isVerticalRuler (line 231) | protected static boolean isVerticalRuler(int flag) {
method getNegativeFlag (line 235) | protected static int getNegativeFlag(int flag) {
method getTextPoint (line 239) | private static PointF getTextPoint(BooheeRuler mParent, int flag, floa...
method getLine (line 272) | private static PointF[] getLine(BooheeRuler mParent, int flag, int len...
method getOutLine (line 311) | private static PointF[] getOutLine(View contentView, int flag, int hei...
method getOutLeftOrRightLine (line 348) | private static PointF[] getOutLeftOrRightLine(View contentView, int fl...
method onSizeChanged (line 411) | @Override
method dispatchTouchEvent (line 420) | @Override
method onTouchEvent (line 426) | @Override
method fling (line 492) | private void fling(boolean isVertical, int vx, int vy) {
method computeScroll (line 508) | @Override
method scrollBackToCurrentScale (line 520) | protected void scrollBackToCurrentScale(boolean isVertical) {
method scroll2Scale (line 530) | protected void scroll2Scale(float scale) {
method scrollTo (line 541) | @Override
method scrollXY2Scale (line 574) | private float scrollXY2Scale(int scrollXY) {
method scale2ScrollXY (line 584) | private int scale2ScrollXY(float scale) {
FILE: ruler/src/main/java/com/keyboard3/RulerCallback.java
type RulerCallback (line 7) | public interface RulerCallback {
method onScaleChanging (line 9) | void onScaleChanging(float scale);
FILE: ruler/src/main/java/com/keyboard3/RulerNumberLayout.java
class RulerNumberLayout (line 19) | public class RulerNumberLayout extends RelativeLayout implements RulerCa...
method RulerNumberLayout (line 34) | public RulerNumberLayout(Context context) {
method RulerNumberLayout (line 39) | public RulerNumberLayout(Context context, AttributeSet attrs) {
method RulerNumberLayout (line 45) | public RulerNumberLayout(Context context, AttributeSet attrs, int defS...
method initAttrs (line 50) | private void initAttrs(Context context, AttributeSet attrs) {
method init (line 66) | private void init(Context context) {
method onAttachedToWindow (line 79) | @Override
method bindRuler (line 91) | public void bindRuler(BooheeRuler booheeRuler) {
method onScaleChanging (line 95) | @Override
FILE: ruler/src/test/java/com/keyboard3/ExampleUnitTest.java
class ExampleUnitTest (line 12) | public class ExampleUnitTest {
method addition_isCorrect (line 13) | @Test
Condensed preview — 52 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (122K chars).
[
{
"path": ".gitignore",
"chars": 126,
"preview": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n/."
},
{
"path": "README.md",
"chars": 3021,
"preview": "# HencoderKeyboard3\n[HenCoder「仿写酷界面」活动——征稿](http://hencoder.com/activity-mock-1) <br>\n[HenCoder「仿写酷界面」活动——获奖作品点评](http:/"
},
{
"path": "app/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "app/build.gradle",
"chars": 1196,
"preview": "apply plugin: 'com.android.application'\n\nandroid {\n compileSdkVersion 25\n buildToolsVersion '26.0.2'\n defaultCo"
},
{
"path": "app/proguard-rules.pro",
"chars": 932,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "app/src/androidTest/java/com/keyboard3/hencoderProduct/ExampleInstrumentedTest.java",
"chars": 767,
"preview": "package com.keyboard3.hencoderProduct;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegis"
},
{
"path": "app/src/main/AndroidManifest.xml",
"chars": 698,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/MainActivity.java",
"chars": 2398,
"preview": "package com.keyboard3.hencoderProduct;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.MessageQu"
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/PageFragment.java",
"chars": 1396,
"preview": "package com.keyboard3.hencoderProduct;\n\nimport android.os.Bundle;\nimport android.support.annotation.LayoutRes;\nimport an"
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/Utils.java",
"chars": 303,
"preview": "package com.keyboard3.hencoderProduct;\n\nimport android.content.res.Resources;\nimport android.util.DisplayMetrics;\n\npubli"
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/filpboard/FlipboardLayout.java",
"chars": 2256,
"preview": "package com.keyboard3.hencoderProduct.filpboard;\n\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectA"
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/filpboard/FlipboardView.java",
"chars": 3373,
"preview": "package com.keyboard3.hencoderProduct.filpboard;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport"
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/like/LikeImageView.java",
"chars": 6130,
"preview": "package com.keyboard3.hencoderProduct.like;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport andr"
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/like/LikeLayout.java",
"chars": 1315,
"preview": "package com.keyboard3.hencoderProduct.like;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport an"
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/like/LikeNumView.java",
"chars": 4700,
"preview": "package com.keyboard3.hencoderProduct.like;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport andr"
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/like/LikeView.java",
"chars": 4207,
"preview": "package com.keyboard3.hencoderProduct.like;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListene"
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/movement/MoveLayout.java",
"chars": 1215,
"preview": "package com.keyboard3.hencoderProduct.movement;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimpor"
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/movement/MoveView.java",
"chars": 9772,
"preview": "package com.keyboard3.hencoderProduct.movement;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorSet"
},
{
"path": "app/src/main/java/com/keyboard3/hencoderProduct/ruler/RulerLayout.java",
"chars": 606,
"preview": "package com.keyboard3.hencoderProduct.ruler;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport a"
},
{
"path": "app/src/main/res/layout/activity_main.xml",
"chars": 876,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmln"
},
{
"path": "app/src/main/res/layout/filpboard_layout.xml",
"chars": 904,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.keyboard3.hencoderProduct.filpboard.FlipboardLayout xmlns:android=\"http://sc"
},
{
"path": "app/src/main/res/layout/fragment_page.xml",
"chars": 428,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n andr"
},
{
"path": "app/src/main/res/layout/like_layout.xml",
"chars": 2801,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.keyboard3.hencoderProduct.like.LikeLayout xmlns:android=\"http://schemas.andr"
},
{
"path": "app/src/main/res/layout/move_layout.xml",
"chars": 895,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.keyboard3.hencoderProduct.movement.MoveLayout xmlns:android=\"http://schemas."
},
{
"path": "app/src/main/res/layout/ruler_layout.xml",
"chars": 5123,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.keyboard3.hencoderProduct.ruler.RulerLayout xmlns:android=\"http://schemas.an"
},
{
"path": "app/src/main/res/values/colors.xml",
"chars": 208,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorPrimary\">#3F51B5</color>\n <color name=\"color"
},
{
"path": "app/src/main/res/values/strings.xml",
"chars": 321,
"preview": "<resources>\n <string name=\"app_name\">HenCoder参赛作品by keyboard3</string>\n <string name=\"title_flipboard\">Flipboard翻页"
},
{
"path": "app/src/main/res/values/styles.xml",
"chars": 1068,
"preview": "<resources>\n <declare-styleable name=\"LikeView\">\n <attr name=\"likeNum\" format=\"integer\"></attr>\n <attr "
},
{
"path": "app/src/test/java/com/keyboard3/hencoderProduct/ExampleUnitTest.java",
"chars": 407,
"preview": "package com.keyboard3.hencoderProduct;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local "
},
{
"path": "build.gradle",
"chars": 602,
"preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n r"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 230,
"preview": "#Fri Oct 13 16:17:40 CST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "gradle.properties",
"chars": 730,
"preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
},
{
"path": "gradlew",
"chars": 4987,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "gradlew.bat",
"chars": 2330,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "ruler/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "ruler/build.gradle",
"chars": 835,
"preview": "apply plugin: 'com.android.library'\n\nandroid {\n compileSdkVersion 25\n buildToolsVersion '26.0.2'\n defaultConfig"
},
{
"path": "ruler/proguard-rules.pro",
"chars": 751,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "ruler/src/androidTest/java/com/keyboard3/ExampleInstrumentedTest.java",
"chars": 732,
"preview": "package com.keyboard3;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport andr"
},
{
"path": "ruler/src/main/AndroidManifest.xml",
"chars": 100,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.keyboard3\" />\n"
},
{
"path": "ruler/src/main/java/com/keyboard3/BooheeRuler.java",
"chars": 12248,
"preview": "package com.keyboard3;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.C"
},
{
"path": "ruler/src/main/java/com/keyboard3/InnerRuler.java",
"chars": 20490,
"preview": "package com.keyboard3;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\ni"
},
{
"path": "ruler/src/main/java/com/keyboard3/RulerCallback.java",
"chars": 222,
"preview": "package com.keyboard3;\n\n/**\n * Created by yany on 2017/10/16.\n */\n\npublic interface RulerCallback {\n //选取刻度变化的时候回调\n "
},
{
"path": "ruler/src/main/java/com/keyboard3/RulerNumberLayout.java",
"chars": 3397,
"preview": "package com.keyboard3;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.support.an"
},
{
"path": "ruler/src/main/res/drawable/cursor_shape.xml",
"chars": 232,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:sha"
},
{
"path": "ruler/src/main/res/layout/layout_kg_number.xml",
"chars": 702,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n an"
},
{
"path": "ruler/src/main/res/values/attr.xml",
"chars": 1937,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <declare-styleable name=\"BooheeRuler\">\n <attr name=\"minSca"
},
{
"path": "ruler/src/main/res/values/colors.xml",
"chars": 255,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorGray\">#e2e5e2</color>\n <color name=\"colorDir"
},
{
"path": "ruler/src/main/res/values/strings.xml",
"chars": 68,
"preview": "<resources>\n <string name=\"app_name\">Ruler</string>\n</resources>\n"
},
{
"path": "ruler/src/test/java/com/keyboard3/ExampleUnitTest.java",
"chars": 391,
"preview": "package com.keyboard3;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which"
},
{
"path": "settings.gradle",
"chars": 26,
"preview": "include ':app', ':ruler'\n"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the keyboard3/HencoderKeyboard3 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 52 files (106.2 KB), approximately 29.3k tokens, and a symbol index with 192 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.