Repository: hougr/SmartisanPull
Branch: master
Commit: 99ef6cd89c31
Files: 27
Total size: 57.1 KB
Directory structure:
gitextract_9p1t0505/
├── .gitignore
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── hougr/
│ │ └── smartisanpull/
│ │ └── ApplicationTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── hougr/
│ │ │ └── smartisanpull/
│ │ │ ├── MainActivity.java
│ │ │ ├── RefreshStatus.java
│ │ │ ├── SmartisanCircleView.java
│ │ │ └── SmartisanRefreshableLayout.java
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── listview_item.xml
│ │ │ └── refreshablelayout_header.xml
│ │ ├── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── values-w820dp/
│ │ └── dimens.xml
│ └── test/
│ └── java/
│ └── com/
│ └── hougr/
│ └── smartisanpull/
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
/.idea
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
================================================
FILE: README.md
================================================
# 锤子下拉
[](https://android-arsenal.com/details/3/4257)
“锤子下拉”,东半球最优雅的下拉控件。(也叫SmartisanRefreshableLayout。)
涉及到的知识点包括:自定义View、自定义ViewGroup、事件分发、属性动画等。
# 说明
本项目模仿“锤子阅读”的下拉效果。仅供学习交流,请勿用于商业用途。
Because the animation in my project imitates the one in Smartisan OS. Only communicate using, please do not used for commercial purposes or illegal purposes, thank you!
# 介绍
下面是我做的过程中,每个阶段有意思的地方:
(下面把“可刷新”的最小距离叫刷新距离)
| 阶段 | 该阶段截图 | 该阶段说明 |
| --------------- | ------------------ | --------------- |
| 刚开始 |  | 下拉时先把item0上面的分隔线滚动出来,该分隔线在下拉过程中一直显示,直到header完全消失,它才重新藏起来。还有,到达刷新距离前,提示语逐渐清晰。另外,在任何阶段,如果手指向上返回,动画逐渐回到原始状态。 |
| 到达刷新距离前 |  | 两线段逐渐过渡为圆弧。 |
| 到达刷新距离时 |  | 两圆弧刚好各转半圈,两圆弧间的两个缺口处于同一水平线。 |
| 到达刷新距离以下 |  | 刷新距离以下,摩擦系数越来越大。但是,两圆弧的旋转始终是平滑的,只有速度变化。 |
| 刷新完成后 |  | 最后,两圆弧逐渐过渡成线段,消失在两端。 |
# 使用
### 首先,在布局文件中使用SmartisanRefreshableLayout
只需在里面加入ListView,仍然保持了ListView的正常使用,不会对它造成什么影响。
```
```
### 然后,实现下拉事件的监听
```
mSmartisanRefreshableLayout = (SmartisanRefreshableLayout) findViewById(R.id.refreshable_view);
mSmartisanRefreshableLayout.setOnRefreshListener(new SmartisanRefreshableLayout.PullToRefreshListener() {
@Override
public void onRefresh() {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onRefreshFinished() {
mSmartisanRefreshableLayout.finishRefreshing();
mViewHolderAdapter.addToListHead((mRefreshCount++)+" 喜欢的话,可以在github上赏我一颗star");
}
});
```
# 接下来
接下来,会增加对RecyclerView的支持。
# 拜托
喜欢的话,可以点击右上角的star。感谢。
# License 代码版权
The MIT License (MIT)
Copyright (c) 2016 hougr
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "com.hougr.smartisanpull"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.1.1'
}
================================================
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/hougr/Library/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 *;
#}
================================================
FILE: app/src/androidTest/java/com/hougr/smartisanpull/ApplicationTest.java
================================================
package com.hougr.smartisanpull;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* Testing Fundamentals
*/
public class ApplicationTest extends ApplicationTestCase {
public ApplicationTest() {
super(Application.class);
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/com/hougr/smartisanpull/MainActivity.java
================================================
package com.hougr.smartisanpull;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.LinkedList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private Toolbar mToolbar;
private SmartisanRefreshableLayout mSmartisanRefreshableLayout;
private ListView mListView;
private int mRefreshCount =0;
private LinkedList mStringList;
private ViewHolderAdapter mViewHolderAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.toolBar);
mToolbar.setTitle("");
setSupportActionBar(mToolbar);
mSmartisanRefreshableLayout = (SmartisanRefreshableLayout) findViewById(R.id.refreshable_view);
mSmartisanRefreshableLayout.setOnRefreshListener(new SmartisanRefreshableLayout.PullToRefreshListener() {
@Override
public void onRefresh() {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onRefreshFinished() {
mSmartisanRefreshableLayout.finishRefreshing();
mViewHolderAdapter.addToListHead((mRefreshCount++)+" 喜欢的话,可以在github上赏我一颗star");
}
});
mStringList = new LinkedList<>();
mStringList.add("“锤子下拉”,东半球最优雅的下拉控件");
mStringList.add("https://github.com/hougr/SmartisanPull");
mStringList.add("模仿“锤子阅读”的下拉效果");
mStringList.add("仅供学习交流,请勿用于商业用途。");
mStringList.add("----------分隔线----------");
mStringList.add("下面是我做的过程中觉得有意思的地方:");
mStringList.add("(下面把“可刷新”的最小距离叫刷新距离)");
mStringList.add("1、下拉时先把item0上面的分隔线滚动出来,");
mStringList.add(" 该分隔线在下拉过程中一直显示,");
mStringList.add(" 直到header完全消失,它才重新藏起来");
mStringList.add("2、到达刷新距离前,提示语逐渐清晰");
mStringList.add("3、到达刷新距离时,两圆弧刚好各转半圈,");
mStringList.add(" 两圆弧间的两个缺口处于同一水平线");
mStringList.add("4、刷新距离以下,摩擦系数越来越大");
mStringList.add("5、如果手指向上返回,动画逐渐回到原始状态");
mStringList.add("6、两圆弧的旋转始终是平滑的,只有速度变化");
mStringList.add("7、最后,两圆弧逐渐过渡成线段,消失在两端");
mStringList.add("喜欢的话,可以在github上赏我一颗star");
mListView = (ListView) findViewById(R.id.list_view);
mViewHolderAdapter = new ViewHolderAdapter(getApplicationContext(),mStringList);
mListView.setAdapter(mViewHolderAdapter);
}
private class ViewHolderAdapter extends BaseAdapter {
public List mItemStringList;
public LayoutInflater mLayoutInflater;
ViewHolderAdapter(Context context, List stringList){
mItemStringList =stringList;
mLayoutInflater = LayoutInflater.from(context);
}
public void addToListHead(String newItemString){
mItemStringList.add(0,newItemString);
notifyDataSetChanged();
}
@Override
public int getCount() {
if(mItemStringList ==null){
return 0;
}
return mItemStringList.size();
}
@Override
public Object getItem(int i) {
return mItemStringList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
final String itemString= mItemStringList.get(i);
ViewHolder viewHolder;
if(view == null){
viewHolder=new ViewHolder();
view = mLayoutInflater.inflate(R.layout.listview_item,null);
viewHolder.mItemView = view;
viewHolder.mTextView =(TextView)view.findViewById(R.id.textView);
view.setTag(viewHolder);
}else {
viewHolder=(ViewHolder) view.getTag();
}
viewHolder.mTextView.setText(itemString);
viewHolder.mItemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(),"点击了:"+itemString,Toast.LENGTH_SHORT).show();
}
});
return view;
}
public final class ViewHolder{
View mItemView;
TextView mTextView;
}
}
}
================================================
FILE: app/src/main/java/com/hougr/smartisanpull/RefreshStatus.java
================================================
package com.hougr.smartisanpull;
/**
* Created by hougr on 16/8/29.
*/
public class RefreshStatus{ //四种状态。
public static final int STATUS_ORIGIN = 0;
public static final int STATUS_DISTANCE_UNFINISHED = 1;
public static final int STATUS_DISTANCE_UNFINISHED_BACK = 2;
public static final int STATUS_DISTANCE_FINISHED = 3;
public static final int STATUS_REFRESH_PREPARE = 4;
public static final int STATUS_REFRESHING =5;
public static final int STATUS_REFRESH_FINISHED_CIRCLE = 6;
// public static final int STATUS_REFRESH_FINISHED_HIDE = 7;
}
================================================
FILE: app/src/main/java/com/hougr/smartisanpull/SmartisanCircleView.java
================================================
package com.hougr.smartisanpull;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by hougr on 16/8/25.
*/
public class SmartisanCircleView extends View {
// public static final float LINE_LENGTH = 45;
// private static final float ARROW_LENGTH = 8;
// public static final float CIRCLE_RADIUS = 20;
float Width;
float Height;
public float mLineLength;
public float mArrowLength;
public float mCircleRadius;
public float mStrokeWidth;
public float mLineSpaceHalf;
Paint mPaint;
private int mRefreshStatus;
private float mAnimatorDistance;
// private long animatorDuration = 5000;
// private TimeInterpolator timeInterpolator = new DecelerateInterpolator();
// private TimeInterpolator timeInterpolator = new LinearInterpolator();
public SmartisanCircleView(Context context, AttributeSet attrs) {
super(context, attrs);
mRefreshStatus = RefreshStatus.STATUS_ORIGIN;
mAnimatorDistance =0;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Width = getWidth();
Height = getHeight();
mCircleRadius = Height/2f/(float) (2* Math.PI);
mStrokeWidth = mCircleRadius/3f;
mArrowLength = mStrokeWidth * 1.2f;
mLineLength = mCircleRadius * (float) Math.PI * 6.2f/8f;
mLineSpaceHalf = (mCircleRadius * (float) Math.PI - mLineLength)/2;
mPaint=new Paint();
mPaint.setStyle(Paint.Style.STROKE);//设置画笔样式为描边,如果已经设置,可以忽略
// mPaint.setColor(Color.GREEN);
mPaint.setColor(Color.argb(255,200, 200, 200));
mPaint.setStrokeCap(Paint.Cap.ROUND);//圆角笔触
// mPaint.setStrokeWidth(10);
mPaint.setStrokeWidth(mStrokeWidth);
// updateCircleView(animatorDuration);
}
// public float getViewHeight(){
// return Height;
// }
public void setStatusAndAnimatorDistance(int status, float animatorDistance){
mRefreshStatus = status;
mAnimatorDistance = animatorDistance;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(Width/2,Height/2);
switch (mRefreshStatus){
case RefreshStatus.STATUS_ORIGIN:
break;
case RefreshStatus.STATUS_DISTANCE_UNFINISHED:
drawDistanceFinished(canvas);
break;
case RefreshStatus.STATUS_DISTANCE_UNFINISHED_BACK:
drawDistanceFinished(canvas);
break;
case RefreshStatus.STATUS_DISTANCE_FINISHED:
drawDistanceFinished(canvas);
break;
case RefreshStatus.STATUS_REFRESH_PREPARE:
drawRefreshing(canvas);
break;
case RefreshStatus.STATUS_REFRESHING:
drawRefreshing(canvas);
break;
case RefreshStatus.STATUS_REFRESH_FINISHED_CIRCLE:
drawRefreshFinished(canvas);
break;
// case RefreshStatus.STATUS_REFRESH_FINISHED_HIDE:
// drawRefreshFinished(canvas);
// break;
default:
break;
}
}
private void drawDistanceFinished(Canvas canvas){
mAnimatorDistance = mAnimatorDistance - mLineSpaceHalf*2;
if(mAnimatorDistance < Height/2 ){
float leftStartX = -mCircleRadius;
float leftStartY = Height/4 - mAnimatorDistance/2;
float leftStopX = leftStartX;
float leftStopY = leftStartY + mLineLength;
canvas.drawLine(leftStartX, leftStartY, leftStopX, leftStopY, mPaint);
float leftArrowX = leftStartX - mArrowLength * (float) Math.sin(Math.toRadians(30));
float leftArrowY = leftStartY + mArrowLength * (float) Math.cos(Math.toRadians(30));
canvas.drawLine(leftStartX, leftStartY, leftArrowX, leftArrowY, mPaint);
float rightStartX = -leftStartX;
float rightStartY = -leftStartY;
float rightStopX = -leftStopX;
float rightStopY = -leftStopY;
canvas.drawLine(rightStartX,rightStartY,rightStopX,rightStopY,mPaint);
float rightArrowX = -leftArrowX;
float rightArrowY = -leftArrowY;
canvas.drawLine(rightStartX, rightStartY, rightArrowX, rightArrowY, mPaint);
}else{
float circleValue = (mAnimatorDistance - Height/2)/2;
float radius = mCircleRadius;
RectF rectF = new RectF(-radius,-radius,radius,radius);
float swipeAngle = (float) Math.toDegrees(circleValue/radius);
float totalAngle = (float) Math.toDegrees(mLineLength/radius);
float leftArcStartX = mCircleRadius * (float) Math.cos(Math.toRadians(180+swipeAngle));
float leftArcStartY = mCircleRadius * (float) Math.sin(Math.toRadians(180+swipeAngle));
float leftArcArrowX = leftArcStartX - mArrowLength * (float) Math.sin(Math.toRadians(swipeAngle+30));
float leftArcArrowY = leftArcStartY + mArrowLength * (float) Math.cos(Math.toRadians(swipeAngle+30));
canvas.drawLine(leftArcStartX, leftArcStartY, leftArcArrowX, leftArcArrowY, mPaint);
float rightArcStartX = mCircleRadius * (float) Math.cos(Math.toRadians(swipeAngle));
float rightArcStartY = mCircleRadius * (float) Math.sin(Math.toRadians(swipeAngle));
float rightArcArrowX = rightArcStartX + mArrowLength * (float) Math.sin(Math.toRadians(swipeAngle+30));
float rightArcArrowY = rightArcStartY - mArrowLength * (float) Math.cos(Math.toRadians(swipeAngle+30));
canvas.drawLine(rightArcStartX, rightArcStartY, rightArcArrowX, rightArcArrowY, mPaint);
if(circleValue < totalAngle/360 *(float) Math.PI * mCircleRadius*2 ){
canvas.drawArc(rectF,180,swipeAngle,false,mPaint);
canvas.drawArc(rectF,0,swipeAngle,false,mPaint);
float leftStartX = -mCircleRadius;
float leftStartY = 0;
float leftStopX = leftStartX;
float leftStopY = mLineLength - circleValue;
canvas.drawLine(leftStartX, leftStartY, leftStopX, leftStopY, mPaint);
float rightStartX = -leftStartX;
float rightStartY = -leftStartY;
float rightStopX = -leftStopX;
float rightStopY = -leftStopY;
canvas.drawLine(rightStartX, rightStartY, rightStopX, rightStopY, mPaint);
}else {
canvas.drawArc(rectF,swipeAngle-totalAngle,totalAngle,false,mPaint);
canvas.drawArc(rectF,swipeAngle+180-totalAngle,totalAngle,false,mPaint);
}
}
}
private void drawRefreshing(Canvas canvas){
mAnimatorDistance = mAnimatorDistance - mLineSpaceHalf*2;
float circleValue = (mAnimatorDistance - Height/2)/2;
float radius = mCircleRadius;
RectF rectF = new RectF(-radius,-radius,radius,radius);
float swipeAngle = (float) Math.toDegrees(circleValue/radius);
float totalAngle = (float) Math.toDegrees(mLineLength/radius);
float leftArcStartX = mCircleRadius * (float) Math.cos(Math.toRadians(180+swipeAngle));
float leftArcStartY = mCircleRadius * (float) Math.sin(Math.toRadians(180+swipeAngle));
float leftArcArrowX = leftArcStartX - mArrowLength * (float) Math.sin(Math.toRadians(swipeAngle+30));
float leftArcArrowY = leftArcStartY + mArrowLength * (float) Math.cos(Math.toRadians(swipeAngle+30));
canvas.drawLine(leftArcStartX, leftArcStartY, leftArcArrowX, leftArcArrowY, mPaint);
float rightArcStartX = mCircleRadius * (float) Math.cos(Math.toRadians(swipeAngle));
float rightArcStartY = mCircleRadius * (float) Math.sin(Math.toRadians(swipeAngle));
float rightArcArrowX = rightArcStartX + mArrowLength * (float) Math.sin(Math.toRadians(swipeAngle+30));
float rightArcArrowY = rightArcStartY - mArrowLength * (float) Math.cos(Math.toRadians(swipeAngle+30));
canvas.drawLine(rightArcStartX, rightArcStartY, rightArcArrowX, rightArcArrowY, mPaint);
canvas.drawArc(rectF,swipeAngle-totalAngle,totalAngle,false,mPaint);
canvas.drawArc(rectF,swipeAngle+180-totalAngle,totalAngle,false,mPaint);
}
private void drawRefreshFinished(Canvas canvas){
mAnimatorDistance = mAnimatorDistance - mLineSpaceHalf;
if(mAnimatorDistance < (float) Math.PI * mCircleRadius){
float radius = mCircleRadius;
RectF rectF = new RectF(-radius,-radius,radius,radius);
float swipeAngle = (float) Math.toDegrees(mAnimatorDistance/radius);
float totalAngle = (float) Math.toDegrees(mLineLength/radius);
float leftArcStartX = mCircleRadius * (float) Math.cos(Math.toRadians(180+swipeAngle));
float leftArcStartY = mCircleRadius * (float) Math.sin(Math.toRadians(180+swipeAngle));
float leftArcArrowX = leftArcStartX - mArrowLength * (float) Math.sin(Math.toRadians(swipeAngle+30));
float leftArcArrowY = leftArcStartY + mArrowLength * (float) Math.cos(Math.toRadians(swipeAngle+30));
canvas.drawLine(leftArcStartX, leftArcStartY, leftArcArrowX, leftArcArrowY, mPaint);
float rightArcStartX = mCircleRadius * (float) Math.cos(Math.toRadians(swipeAngle));
float rightArcStartY = mCircleRadius * (float) Math.sin(Math.toRadians(swipeAngle));
float rightArcArrowX = rightArcStartX + mArrowLength * (float) Math.sin(Math.toRadians(swipeAngle+30));
float rightArcArrowY = rightArcStartY - mArrowLength * (float) Math.cos(Math.toRadians(swipeAngle+30));
canvas.drawLine(rightArcStartX, rightArcStartY, rightArcArrowX, rightArcArrowY, mPaint);
canvas.drawArc(rectF,swipeAngle-totalAngle,totalAngle,false,mPaint);
canvas.drawArc(rectF,swipeAngle+180-totalAngle,totalAngle,false,mPaint);
}else if(mAnimatorDistance < (float) Math.PI * mCircleRadius + mLineLength){
float nonCircleValue = mAnimatorDistance - (float) Math.PI * mCircleRadius;
float leftStartX = -mCircleRadius;
float leftStartY = -nonCircleValue;
float leftStopX = leftStartX;
float leftStopY = 0;
canvas.drawLine(leftStartX, leftStartY, leftStopX, leftStopY, mPaint);
float leftArcArrowX = leftStartX - mArrowLength * (float) Math.sin(Math.toRadians(30));
float leftArcArrowY = leftStartY + mArrowLength * (float) Math.cos(Math.toRadians(30));
canvas.drawLine(leftStartX, leftStartY, leftArcArrowX, leftArcArrowY, mPaint);
float rightStartX = -leftStartX;
float rightStartY = -leftStartY;
float rightStopX = -leftStopX;
float rightStopY = -leftStopY;
canvas.drawLine(rightStartX, rightStartY, rightStopX, rightStopY, mPaint);
float rightArcArrowX = rightStartX + mArrowLength * (float) Math.sin(Math.toRadians(30));
float rightArcArrowY = rightStartY - mArrowLength * (float) Math.cos(Math.toRadians(30));
canvas.drawLine(rightStartX, rightStartY, rightArcArrowX, rightArcArrowY, mPaint);
float radius = mCircleRadius;
RectF rectF = new RectF(-radius,-radius,radius,radius);
float swipeAngle = (float) Math.toDegrees((mLineLength-nonCircleValue)/radius);
float smallAngle = 180 - swipeAngle;
canvas.drawArc(rectF,smallAngle,swipeAngle,false,mPaint);
canvas.drawArc(rectF,smallAngle+180,swipeAngle,false,mPaint);
}else{
mAnimatorDistance = mAnimatorDistance - (float) Math.PI * mCircleRadius - mLineLength;
float leftStopX = -mCircleRadius;
float leftStopY = - mAnimatorDistance;
float leftStartX = leftStopX;
float leftStartY = leftStopY - mLineLength;
canvas.drawLine(leftStartX, leftStartY, leftStopX, leftStopY, mPaint);
float leftArrowX = leftStartX - mArrowLength * (float) Math.sin(Math.toRadians(30));
float leftArrowY = leftStartY + mArrowLength * (float) Math.cos(Math.toRadians(30));
canvas.drawLine(leftStartX, leftStartY, leftArrowX, leftArrowY, mPaint);
float rightStartX = -leftStartX;
float rightStartY = -leftStartY;
float rightStopX = -leftStopX;
float rightStopY = -leftStopY;
canvas.drawLine(rightStartX,rightStartY,rightStopX,rightStopY,mPaint);
float rightArrowX = -leftArrowX;
float rightArrowY = -leftArrowY;
canvas.drawLine(rightStartX, rightStartY, rightArrowX, rightArrowY, mPaint);
}
}
// protected float dp2px(float dp){
// return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
// }
}
================================================
FILE: app/src/main/java/com/hougr/smartisanpull/SmartisanRefreshableLayout.java
================================================
package com.hougr.smartisanpull;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* Created by hougr on 16/8/25.
*/
public class SmartisanRefreshableLayout extends LinearLayout {
private static final float ZERO_FOR_COMPARE=0.005f;
//所有子View
private RelativeLayout mHeaderLayout;
private SmartisanCircleView mCircleView;
private TextView mDescriptionTextView;
private ListView mListView;
//布局相关
private boolean mIsLayoutLoaded; //是否已加载过一次layout
private int mHeaderHeight;
private MarginLayoutParams mHeaderMargin; //下拉头的布局参数
private ValueAnimator mCircleAnimator;
private ValueAnimator mPulledAnimator;
//事件相关
private int mCurrentStatus;
private boolean mInterceptBoolean = false; //当前事件是否拦截
private float mLastMoveY;
private float mPulledDistance; //HeadView被下拉的距离,不是手指移动距离
private float mAnimatorDistance;
private float mRubRatio = 1.0f; //摩擦系数
private float mListDividerHeight;
private PullToRefreshListener mListener; //下拉刷新的回调接口
public SmartisanRefreshableLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mHeaderLayout = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.refreshablelayout_header,null,true);
mCircleView = (SmartisanCircleView) mHeaderLayout.findViewById(R.id.smartisanView);
mDescriptionTextView = (TextView) mHeaderLayout.findViewById(R.id.descriptionTextView);
setOrientation(VERTICAL);
addView(mHeaderLayout, 0);
mPulledDistance=0;
mAnimatorDistance=0;
}
public void setOnRefreshListener(PullToRefreshListener listener) {
mListener = listener;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed && !mIsLayoutLoaded) {
mHeaderMargin = (MarginLayoutParams) mHeaderLayout.getLayoutParams();
mHeaderHeight = mHeaderLayout.getHeight();
mHeaderMargin.topMargin = -mHeaderHeight/2;
mHeaderMargin.bottomMargin = mHeaderMargin.topMargin;
mHeaderLayout.setLayoutParams(mHeaderMargin);
mListView = (ListView) getChildAt(1); //这句需要改进
mListDividerHeight = mListView.getDividerHeight();
this.setBackgroundColor(Color.argb(255,255,255,255)); //把背景统一设置为白色
mCurrentStatus = RefreshStatus.STATUS_ORIGIN;
updateLayoutAndText();
mIsLayoutLoaded = true;
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int x = (int) event.getRawX();
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mInterceptBoolean = false; //避免不下拉,只是往下翻列表。因为此时不知道手势往上还是往下。
break;
}
case MotionEvent.ACTION_MOVE: {
View firstChildView = mListView.getChildAt(0); //用于判断ListView是否没有元素
if(firstChildView == null || (firstChildView != null && mListView.getFirstVisiblePosition() == 0 && mListView.getChildAt(0).getTop() == 0)){ //万事俱备,只欠下拉。
float currentRawY = event.getRawY();
float tmpMoveY = currentRawY - mLastMoveY;
if((isEqualZero(mPulledDistance) && tmpMoveY >0) || (mPulledDistance > 0 && mCurrentStatus != RefreshStatus.STATUS_REFRESHING)) {
//两种情况:第一次下拉、已经下拉过了。
// 如果首个元素的上边缘,距离父布局值为0,就说明ListView滚动到了最顶部,此时应该允许下拉刷新
Log.d("是否拦截",":可下拉");
if(!mInterceptBoolean){
mLastMoveY = event.getRawY();
}
mInterceptBoolean = true;
// // 当前正处于下拉或释放状态,要让ListView失去焦点,否则被点击的那一项会一直处于选中状态
// mListView.setPressed(false);
// mListView.setFocusable(false);
// mListView.setFocusableInTouchMode(false);
// // 当前正处于下拉或释放状态,通过返回true屏蔽掉ListView的滚动事件
}else if(mCurrentStatus == RefreshStatus.STATUS_REFRESHING && tmpMoveY > 0){
mInterceptBoolean =false;
}else if(isEqualZero(mPulledDistance) && tmpMoveY < 0){
} else{
mInterceptBoolean = false;
}
} else {
mInterceptBoolean = false;
}
break;
}
case MotionEvent.ACTION_UP: {
mInterceptBoolean = false;
break;
}
default:
break;
}
mLastMoveY = y;
Log.d("是否拦截",":"+mInterceptBoolean);
return mInterceptBoolean;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
float currentRawY = event.getRawY();
float tmpMoveY = currentRawY - mLastMoveY;
if(mPulledDistance < 0 || isEqualZero(mPulledDistance)){
mPulledDistance += tmpMoveY;
}else {
if(mPulledDistance <= mHeaderHeight){
mCurrentStatus = RefreshStatus.STATUS_DISTANCE_UNFINISHED;
mRubRatio = 1.0f;
}else {
mCurrentStatus = RefreshStatus.STATUS_DISTANCE_FINISHED;
mRubRatio = (2-mPulledDistance/(float) mHeaderHeight)/4;
}
mPulledDistance += mRubRatio * tmpMoveY;
mAnimatorDistance = mPulledDistance;
updateAllView(mCurrentStatus,mPulledDistance,mAnimatorDistance);
mLastMoveY = currentRawY;
}
break;
case MotionEvent.ACTION_UP:
default:
Log.d("事件","Action_up");
if (mCurrentStatus == RefreshStatus.STATUS_DISTANCE_FINISHED) {
mCurrentStatus = RefreshStatus.STATUS_REFRESH_PREPARE;
updateAllView(mCurrentStatus, mPulledDistance,mAnimatorDistance);
new RefreshingTask().execute();
} else if (mCurrentStatus == RefreshStatus.STATUS_DISTANCE_UNFINISHED) {
mCurrentStatus = RefreshStatus.STATUS_DISTANCE_UNFINISHED_BACK;
updateAllView(mCurrentStatus, mPulledDistance,mAnimatorDistance);
// new HideHeaderTask().execute();
}
break;
}
return true;
}
private void updateLayoutAndText(){
float mockPulledDistance;
if(mPulledDistance <= mListDividerHeight){
mListView.scrollTo(0,(int)mPulledDistance);
mockPulledDistance =0;
}else{
mListView.scrollTo(0,-mListView.getDividerHeight());
mockPulledDistance = mPulledDistance-mListDividerHeight;
}
mHeaderMargin.topMargin = (int)(-mHeaderHeight/2 + mockPulledDistance/2);
mHeaderMargin.bottomMargin = mHeaderMargin.topMargin;
mHeaderLayout.setLayoutParams(mHeaderMargin);
switch (mCurrentStatus){
case RefreshStatus.STATUS_ORIGIN:
mDescriptionTextView.setTextColor(Color.argb(0,120,120,120));
break;
case RefreshStatus.STATUS_DISTANCE_UNFINISHED:
case RefreshStatus.STATUS_DISTANCE_UNFINISHED_BACK:
mDescriptionTextView.setText("下拉即可刷新...");
if(mockPulledDistance < mHeaderHeight/4){
mDescriptionTextView.setTextColor(Color.argb(0,120,120,120));
}else {
mDescriptionTextView.setTextColor(Color.argb((int)((mockPulledDistance- (float) mHeaderHeight/4)/(mHeaderHeight*3/4)*255),120,120,120));
}
break;
case RefreshStatus.STATUS_DISTANCE_FINISHED:
mDescriptionTextView.setText("松开即可刷新...");
mDescriptionTextView.setTextColor(Color.argb(255,120,120,120));
break;
case RefreshStatus.STATUS_REFRESH_PREPARE:
case RefreshStatus.STATUS_REFRESHING:
mDescriptionTextView.setText("正在刷新列表...");
mDescriptionTextView.setTextColor(Color.argb(255,120,120,120));
break;
case RefreshStatus.STATUS_REFRESH_FINISHED_CIRCLE:
// case RefreshStatus.STATUS_REFRESH_FINISHED_HIDE:
mDescriptionTextView.setText("正在刷新列表...");
if(mockPulledDistance < mHeaderHeight/4){
mDescriptionTextView.setTextColor(Color.argb(0,120,120,120));
}else {
mDescriptionTextView.setTextColor(Color.argb((int)((mockPulledDistance- (float) mHeaderHeight/4)/(mHeaderHeight*3/4)*255),120,120,120));
}
break;
default:
break;
}
}
public void updateAllView(int refreshStatus, float pulledDistance , final float animatorDistance){
Log.d("更新视图",refreshStatus+","+pulledDistance);
mCurrentStatus = refreshStatus;
mPulledDistance = pulledDistance;
mAnimatorDistance = animatorDistance;
mCircleView.setStatusAndAnimatorDistance(refreshStatus, animatorDistance);
updateLayoutAndText();
switch (mCurrentStatus){
case RefreshStatus.STATUS_ORIGIN:
break;
case RefreshStatus.STATUS_DISTANCE_UNFINISHED:
mCircleView.setStatusAndAnimatorDistance(RefreshStatus.STATUS_DISTANCE_UNFINISHED, mAnimatorDistance);
mCircleView.invalidate();
break;
case RefreshStatus.STATUS_DISTANCE_UNFINISHED_BACK:
resetCircleAnimator(mAnimatorDistance, 0, 300, 0, new UpdateHeaderViewCallback() {
@Override
public void onAnimationUpdate(float animatorValue) {
Log.d("属性动画过程值:","是"+ animatorValue);
mPulledDistance = animatorValue;
mAnimatorDistance = animatorValue;
mCircleView.setStatusAndAnimatorDistance(RefreshStatus.STATUS_DISTANCE_UNFINISHED_BACK, mAnimatorDistance);
mCircleView.invalidate();
updateLayoutAndText();
}
@Override
public void onAnimationEnd() {
mCurrentStatus = RefreshStatus.STATUS_ORIGIN;
mPulledDistance =0;
mAnimatorDistance =mPulledDistance;
mCircleView.setStatusAndAnimatorDistance(RefreshStatus.STATUS_ORIGIN, 0);
}
});
break;
case RefreshStatus.STATUS_DISTANCE_FINISHED:
mCircleView.invalidate();
break;
case RefreshStatus.STATUS_REFRESH_PREPARE:
resetPullAnimator(mPulledDistance, mHeaderHeight, 200, 0, new UpdateHeaderViewCallback() {
@Override
public void onAnimationUpdate(float animatorValue) {
mPulledDistance =animatorValue;
updateLayoutAndText();
}
@Override
public void onAnimationEnd() {
mCurrentStatus = RefreshStatus.STATUS_REFRESHING;
updateAllView(RefreshStatus.STATUS_REFRESHING, mCircleView.getHeight(),mAnimatorDistance);
}
});
resetCircleAnimator(mPulledDistance, mPulledDistance+ (float) (Math.PI * mCircleView.mCircleRadius*2), 350, -1, new UpdateHeaderViewCallback() {
@Override
public void onAnimationUpdate(float animatorValue) {
mAnimatorDistance =animatorValue;
mCircleView.setStatusAndAnimatorDistance(RefreshStatus.STATUS_REFRESHING, mAnimatorDistance);
mCircleView.invalidate();
}
@Override
public void onAnimationEnd() {
}
});
break;
case RefreshStatus.STATUS_REFRESHING:
break;
case RefreshStatus.STATUS_REFRESH_FINISHED_CIRCLE:
float doubleCircleCount = (mAnimatorDistance - mHeaderHeight/2)/((float) Math.PI * mCircleView.mCircleRadius * 2);
float circleCount = doubleCircleCount/2;
float smallCircle = circleCount % 1;
Log.d("刷新后","smallCircle:"+smallCircle);
if(smallCircle >0.5){
smallCircle -=0.5;
}
float nonCircleCount = 0.5f - smallCircle;
float toDoCircleDistance = nonCircleCount * ((float) Math.PI * mCircleView.mCircleRadius * 2);
final float finalTotalDistance = (float) Math.PI * mCircleView.mCircleRadius + mCircleView.mLineLength+mHeaderHeight/4;
final float toDoTotalDistance = toDoCircleDistance + mCircleView.mLineLength+mHeaderHeight/4;
final float bili = mHeaderHeight / toDoTotalDistance;
final float doneDistance = finalTotalDistance - toDoTotalDistance;
mAnimatorDistance = doneDistance;
resetCircleAnimator(mAnimatorDistance, finalTotalDistance, 200, 0, new UpdateHeaderViewCallback() {
@Override
public void onAnimationUpdate(float animatorValue) {
mAnimatorDistance=animatorValue;
mPulledDistance = (finalTotalDistance - animatorValue) * bili;
mCircleView.setStatusAndAnimatorDistance(RefreshStatus.STATUS_REFRESH_FINISHED_CIRCLE, mAnimatorDistance);
mCircleView.invalidate();
updateLayoutAndText();
}
@Override
public void onAnimationEnd() {
mCurrentStatus = RefreshStatus.STATUS_ORIGIN;
mPulledDistance =0;
mAnimatorDistance =mPulledDistance;
mCircleView.setStatusAndAnimatorDistance(RefreshStatus.STATUS_ORIGIN, 0);
}
});
break;
default:
break;
}
}
private void resetCircleAnimator(float startValue, float endValue, int duration , int repeatCount, final UpdateHeaderViewCallback updateHeaderViewCallback){
if (mCircleAnimator !=null && mCircleAnimator.isRunning()){
mCircleAnimator.cancel();
}
mCircleAnimator = ValueAnimator.ofFloat(startValue, endValue).setDuration(duration);
mCircleAnimator.setInterpolator(new LinearInterpolator());
mCircleAnimator.setRepeatCount(repeatCount);
mCircleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
updateHeaderViewCallback.onAnimationUpdate(animatedValue);
}
});
mCircleAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
updateHeaderViewCallback.onAnimationEnd();
animation.cancel();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
mCircleAnimator.start();
}
private void resetPullAnimator(float startValue, float endValue, int duration , int repeatCount, final UpdateHeaderViewCallback updateHeaderViewCallback){
if (mPulledAnimator !=null && mPulledAnimator.isRunning()){
mPulledAnimator.cancel();
}
mPulledAnimator = ValueAnimator.ofFloat(startValue, endValue).setDuration(duration);
mPulledAnimator.setInterpolator(new LinearInterpolator());
mPulledAnimator.setRepeatCount(repeatCount);
mPulledAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
updateHeaderViewCallback.onAnimationUpdate(animatedValue);
}
});
mPulledAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
updateHeaderViewCallback.onAnimationEnd();
animation.cancel();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
mPulledAnimator.start();
}
public void finishRefreshing(){
mCurrentStatus = RefreshStatus.STATUS_REFRESH_FINISHED_CIRCLE;
updateAllView(mCurrentStatus, mHeaderHeight, mAnimatorDistance);
}
private static interface UpdateHeaderViewCallback{
public void onAnimationUpdate(float animatorValue);
public void onAnimationEnd();
}
public interface PullToRefreshListener {
public void onRefresh();
public void onRefreshFinished();
}
class RefreshingTask extends AsyncTask {
@Override
protected Void doInBackground(Void... params) {
if (mListener != null) {
mListener.onRefresh();
}
return null;
}
@Override
protected void onProgressUpdate(Float... topMargin) {
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (mListener != null) {
mListener.onRefreshFinished();
}
}
}
private boolean isEqualZero(float floatValue){
if(floatValue < ZERO_FOR_COMPARE && floatValue > -ZERO_FOR_COMPARE){
return true;
}else {
return false;
}
}
}
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/layout/listview_item.xml
================================================
================================================
FILE: app/src/main/res/layout/refreshablelayout_header.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#ffffff
#000000
#FF4081
#333333
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
16dp
16dp
================================================
FILE: app/src/main/res/values/strings.xml
================================================
锤子下拉
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
64dp
================================================
FILE: app/src/test/java/com/hougr/smartisanpull/ExampleUnitTest.java
================================================
package com.hougr.smartisanpull;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*/
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()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Mon Dec 28 10:00:20 PST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-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.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
include ':app'