1
0
mirror of https://gitee.com/51danju/workclock.git synced 2025-10-25 19:31:26 +08:00

修改走秒样式,增加12/24小时模式切换,可以进入全屏精简及相关动画

This commit is contained in:
wushunlian
2019-02-21 19:15:44 +08:00
parent 156f459bf5
commit f48aca7eb2
98 changed files with 6550 additions and 62 deletions

View File

@@ -12,6 +12,9 @@ import android.os.PowerManager;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyEvent;
@@ -25,8 +28,11 @@ import android.widget.TextView;
import android.widget.Toast;
import com.blankj.utilcode.util.PermissionUtils;
import com.fadai.particlesmasher.ParticleSmasher;
import com.fadai.particlesmasher.SmashAnimator;
import java.text.NumberFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
@@ -43,19 +49,26 @@ import clock.socoolby.com.clock.utils.Player;
import clock.socoolby.com.clock.utils.ScreenManager;
import clock.socoolby.com.clock.model.SharePerferenceModel;
import clock.socoolby.com.clock.pop.TimeSetupPopup;
import clock.socoolby.com.clock.widget.animatorview.AbstractAnimator;
import clock.socoolby.com.clock.widget.animatorview.AnimatorView;
import clock.socoolby.com.clock.widget.animatorview.I_Animator;
import clock.socoolby.com.clock.widget.animatorview.animator.BubbleAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.BubbleCollisionAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.BubbleWhirlPoolAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.ClockAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.DotsLineAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.FireAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.FireworkAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.FluorescenceAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.RainAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.RippleAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.SkyAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.SnowAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.StarFallAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.WaterAnimator;
import clock.socoolby.com.clock.widget.animatorview.animator.WindmillAnimator;
import clock.socoolby.com.clock.widget.textview.AutoScrollTextView;
import clock.socoolby.com.clock.widget.textview.DigitTextView;
public class MainActivity extends Activity implements Handler.Callback, View.OnClickListener, android.view.GestureDetector.OnGestureListener {
@@ -65,6 +78,8 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
public final static int MODE_NORMAL = 200;
public final static int MODE_SETTING_OTHER = 202;
public final static int MODE_HANDUP=203;
public final static int MODE_FULLSCREEN=204;
clock.socoolby.com.clock.widget.animatorview.AnimatorView view;
public final static String[] FONT_NAME_ARRAY={
@@ -94,27 +109,27 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
public final static Map<String,Integer> FONT_SIZE_MAP=new HashMap<>();
static {
FONT_SIZE_MAP.put("cherif",100);
FONT_SIZE_MAP.put("cherif",80);
FONT_SIZE_MAP.put("Cigar Box Guitar",120);
FONT_SIZE_MAP.put("Ciung Wanara Sejati",130);
FONT_SIZE_MAP.put("Ciung Wanara Sejati",110);
FONT_SIZE_MAP.put("crystallightning",150);
FONT_SIZE_MAP.put("DK Bergelmir",120);
FONT_SIZE_MAP.put("ds_digi",140);
FONT_SIZE_MAP.put("Granite Rock St",120);
FONT_SIZE_MAP.put("Granite Rock St",110);
FONT_SIZE_MAP.put("GROOT",120);
FONT_SIZE_MAP.put("LCD-U",130);
FONT_SIZE_MAP.put("MILKDROP",120);
FONT_SIZE_MAP.put("MILKDROP",110);
FONT_SIZE_MAP.put("Pro Display tfb",120);
FONT_SIZE_MAP.put("SailingJunco",130);
FONT_SIZE_MAP.put("scoreboard",120);
FONT_SIZE_MAP.put("SFWasabi-Bold",120);
FONT_SIZE_MAP.put("SFWasabi-Bold",110);
FONT_SIZE_MAP.put("the_vandor_spot",140);
FONT_SIZE_MAP.put("WarioLand4CHMC",120);
FONT_SIZE_MAP.put("Woodplank",120);
FONT_SIZE_MAP.put("Xtra Power",110);
FONT_SIZE_MAP.put("Xtra Power",100);
}
private TextView tv_time;
@@ -126,6 +141,9 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
private ImageButton tv_handup;
private TextView tv_handup_image;
private TextView tv_hand_time;
private boolean hand_time_visable=true;
private Timer timer;
private Handler handler;
private final static int UPDATE_TIME = 100;
@@ -137,6 +155,8 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
private int currectAnimatorIndex=0;
private ImageButton tv_hours_system;
//颜色相关
private ImageButton tv_background_color;
private ImageButton tv_foreground_color;
@@ -194,9 +214,26 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
animatorView=findViewById(R.id.tv_background_animatorview);
tv_time = findViewById(R.id.tv_time);
//tv_time.setOnClickListener(this);
tv_date = findViewById(R.id.tv_date);
tv_day = findViewById(R.id.tv_day);
tv_date.setOnClickListener(this);
tv_day = findViewById(R.id.tv_day);
tv_day.setOnClickListener(this);
tv_day.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(screenLock)
return true;
model.setHourSystem12(!model.isHourSystem12());
model.save();
updateHourSystem();
return true;
}
});
tv_weather = findViewById(R.id.tv_weather);
tv_weather.setOnClickListener(this);
tv_descript = findViewById(R.id.tv_descript);
@@ -206,10 +243,24 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
tv_handup.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if(screenLock)
return true;
setupHandUpTime();
return true;
}
});
tv_hand_time=findViewById(R.id.tv_hand_time);
tv_hand_time.setOnClickListener(this);
tv_hand_time.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
hand_time_visable=false;
tv_hand_time.setVisibility(View.GONE);
return true;
}
});
tv_hours_system=findViewById(R.id.tv_hours_system);
tv_background_color=findViewById(R.id.tv_background_color);
tv_background_color.setOnClickListener(this);
@@ -219,12 +270,15 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
tv_foreground_color.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(screenLock)
return true;
if (colorPickerDialog == null)
colorPickerDialog = new ColorPickerPop(MainActivity.this);
colorPickerDialog.setOnColorChangeListenter(new ColorPickerPop.OnColorListener() {
@Override
public void onEnsure(int color) {
model.setForegroundColor(color);
model.save();
setForegroundColor(color);
@@ -245,6 +299,8 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
tv_foreground_color1.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(screenLock)
return true;
if (colorPickerDialog == null)
colorPickerDialog = new ColorPickerPop(MainActivity.this);
@@ -274,12 +330,20 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
handler = new Handler(this);
tv_setting = (ImageButton) findViewById(R.id.tv_setting);
tv_setting.setOnClickListener(this);
//tv_time.setOnClickListener(this);
//RelativeLayout rel_main = (RelativeLayout) findViewById(R.id.rel_main);
//rel_main.setOnClickListener(this);
tv_screen_lock = (ImageButton) findViewById(R.id.tv_screen_lock);
tv_screen_lock.setOnClickListener(this);
tv_screen_lock.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
screenLock(true);
switchMode(MODE_FULLSCREEN);
return true;
}
});
if(PermissionUtils.isGranted("android.permission.WAKE_LOCK","android.permission.DEVICE_POWER")) {
@@ -359,9 +423,10 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
fontIndex =model.getFontIndex()==null?4:model.getFontIndex();
brightness=getSystemBrightness();
handUpAbla=model.isHandUpAble();
resetColorFromModel();
upHandStatic();
resetHandUpTime();
resetColorFromModel();
updateHourSystem();
}
private void resetColorFromModel(){
@@ -372,6 +437,14 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
animatorView.setColor(model.getForegroundColor1());
}
private void updateHourSystem(){
if(model.isHourSystem12())
tv_hours_system.setVisibility(View.VISIBLE);
else
tv_hours_system.setVisibility(View.GONE);
}
//将该Activity上的触碰事件交给GestureDetector处理
@Override
public boolean onTouchEvent(MotionEvent me)
@@ -425,10 +498,14 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
}
protected void setFont(int index){
String timeString=tv_time.getText().toString();
tv_time.setText("", TextView.BufferType.NORMAL);
Log.d(TAG,"befor setFont text:"+timeString+"\t witch:"+tv_time.getWidth()+"\t height:"+tv_time.getHeight());
Log.d(TAG,"setFont index:"+index+"/t name:"+FONT_NAME_ARRAY[index]+".ttf/t size:"+FONT_SIZE_MAP.get(FONT_NAME_ARRAY[index]));
FontUtils.getInstance().replaceFontFromAsset(tv_time,"fonts/"+FONT_NAME_ARRAY[index]+".ttf");
tv_time.setTextSize(FONT_SIZE_MAP.get(FONT_NAME_ARRAY[index]));
FontUtils.getInstance().replaceFontFromAsset(tv_date,"fonts/"+FONT_NAME_ARRAY[index]+".ttf");
tv_time.setTextSize(FONT_SIZE_MAP.get(FONT_NAME_ARRAY[index]));
Log.d(TAG,"after setFont text:"+tv_time.getText()+"\t witch:"+tv_time.getWidth()+"\t height:"+tv_time.getHeight());
}
protected void setForegroundColor(Integer color){
@@ -446,6 +523,8 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
tv_screen_lock.setColorFilter(color);
tv_setting.setColorFilter(color);
tv_background_color.setColorFilter(color);
tv_hand_time.setTextColor(color);
tv_hours_system.setColorFilter(color);
}
protected void setBackGroundColor(Integer color){
@@ -458,11 +537,13 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
CalendarPopup calendarPopup;
WeatherPopup weatherPopup;
ParticleSmasher particleSmasher;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void onClick(View view) {
if(screenLock&&view.getId()!=R.id.tv_screen_lock&&view.getId()!=R.id.tv_handup_image)
if(screenLock&&view.getId()!=R.id.tv_screen_lock&&view.getId()!=R.id.tv_handup_image) {
return;
}
switch (view.getId()) {
case R.id.tv_setting:
setup();
@@ -478,9 +559,16 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
switchMode(MODE_NORMAL);
}
break;
case R.id.tv_hand_time:
setupTempHandUpTime();
break;
case R.id.tv_handup_image:
switchMode(MODE_NORMAL);
break;
case R.id.tv_day:
model.setDisplaySecond(!model.isDisplaySecond());
model.save();
break;
case R.id.tv_date:
if( calendarPopup ==null)
calendarPopup =new CalendarPopup(this);
@@ -494,9 +582,9 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
weatherPopup.showPopupWindow();
break;
case R.id.tv_screen_lock:
screenLock=!screenLock;
tv_screen_lock.setImageResource(screenLock?R.drawable.ic_screen_lock:R.drawable.ic_screen_unlock);
setUpProximityService();
if(isFullScreen)
fullScreen(false);
screenLock(!screenLock);
break;
case R.id.tv_descript:
setDiscript(SettingActivity.roundAutoQuotes());
@@ -529,16 +617,34 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
}
public void screenLock(boolean locked){
screenLock=locked;
tv_screen_lock.setImageResource(screenLock?R.drawable.ic_screen_lock:R.drawable.ic_screen_unlock);
setUpProximityService();
}
private int prevMode=MODE_NORMAL;
private boolean isArtificialHiddle=false;
private boolean isFullScreen=false;
private void switchMode(int mode) {
Log.d(TAG,"switch mode to:"+mode);
switch (mode) {
case MODE_FULLSCREEN:
fullScreen(true);
break;
case MODE_NORMAL:
tv_time.setVisibility(View.VISIBLE);
tv_handup_image.setVisibility(View.GONE);
tv_setting.setVisibility(View.GONE);
tv_background_color.setVisibility(View.GONE);
tv_foreground_color.setVisibility(View.GONE);
tv_foreground_color1.setVisibility(View.GONE);
if(isFullScreen){
if(!isArtificialHiddle)
tv_time.setVisibility(View.VISIBLE);
tv_handup_image.setVisibility(View.GONE);
}else {
tv_time.setVisibility(View.VISIBLE);
tv_handup_image.setVisibility(View.GONE);
tv_setting.setVisibility(View.GONE);
tv_background_color.setVisibility(View.GONE);
tv_foreground_color.setVisibility(View.GONE);
tv_foreground_color1.setVisibility(View.GONE);
}
break;
case MODE_SETTING_OTHER:
tv_time.setVisibility(View.VISIBLE);
@@ -549,17 +655,60 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
tv_foreground_color1.setVisibility(View.VISIBLE);
break;
case MODE_HANDUP:
tv_time.setVisibility(View.GONE);
tv_handup_image.setVisibility(View.VISIBLE);
tv_setting.setVisibility(View.GONE);
tv_background_color.setVisibility(View.GONE);
tv_foreground_color.setVisibility(View.GONE);
tv_foreground_color1.setVisibility(View.GONE);
setDiscriptForModel();
if(isFullScreen){
tv_time.setVisibility(View.GONE);
tv_handup_image.setVisibility(View.VISIBLE);
}else {
tv_time.setVisibility(View.GONE);
tv_handup_image.setVisibility(View.VISIBLE);
tv_setting.setVisibility(View.GONE);
tv_background_color.setVisibility(View.GONE);
tv_foreground_color.setVisibility(View.GONE);
tv_foreground_color1.setVisibility(View.GONE);
setDiscriptForModel();
}
}
prevMode=mMode;
mMode = mode;
}
private void fullScreen(boolean fullable){
isFullScreen=fullable;
if(fullable) {
// @tudo 可对其时行位置移动
//tv_time.setVisibility(View.GONE);
tv_date.setVisibility(View.GONE);
tv_day.setVisibility(View.GONE);
tv_weather.setVisibility(View.GONE);
tv_descript.setVisibility(View.GONE);
tv_handup_image.setVisibility(View.GONE);
tv_handup.setVisibility(View.GONE);
tv_setting.setVisibility(View.GONE);
tv_background_color.setVisibility(View.GONE);
//tv_hand_time.setTextColor(color);
tv_background_color.setVisibility(View.GONE);
tv_foreground_color.setVisibility(View.GONE);
tv_foreground_color1.setVisibility(View.GONE);
}else{
tv_time.setVisibility(View.VISIBLE);
tv_date.setVisibility(View.VISIBLE);
tv_day.setVisibility(View.VISIBLE);
tv_weather.setVisibility(View.VISIBLE);
tv_descript.setVisibility(View.VISIBLE);
tv_handup_image.setVisibility(View.VISIBLE);
tv_handup.setVisibility(View.VISIBLE);
if(prevMode==MODE_HANDUP)
switchMode(MODE_NORMAL);
else
switchMode(prevMode);
}
}
StarFallAnimator starAnimator;
SkyAnimator skyAnimator;
RainAnimator rainAnimator;
@@ -570,6 +719,11 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
BubbleCollisionAnimator bubbleCollisionAnimator;
FireworkAnimator fireworkAnimator;
DotsLineAnimator dotsLineAnimator;
WaterAnimator waterAnimator;
FireAnimator fireAnimator;
ClockAnimator clockAnimator;
RippleAnimator rippleAnimator;
WindmillAnimator windmillAnimator;
public void changeBackGroundAnimator(int index){
switch(index){
case 0:
@@ -582,13 +736,13 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
backGroundAnimator.setRandColor(false);
break;
case 2:
if(skyAnimator==null) {
skyAnimator = new SkyAnimator();
skyAnimator.init(animatorView.getContext(),animatorView);
skyAnimator.setColor(foregroundColor);
if(clockAnimator ==null) {
clockAnimator = new ClockAnimator();
clockAnimator.init(animatorView.getContext(),animatorView);
clockAnimator.setColor(foregroundColor);
}
backGroundAnimator=skyAnimator;
backGroundAnimator.setRandColor(false);
backGroundAnimator= clockAnimator;
//backGroundAnimator.setRandColor(true);
break;
case 3:
if(rainAnimator==null) {
@@ -734,7 +888,7 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
backGroundAnimator= dotsLineAnimator;
backGroundAnimator.setRandColor(false);
break;
case 1:
case 19:
if(dotsLineAnimator ==null) {
dotsLineAnimator = new DotsLineAnimator();
dotsLineAnimator.init(animatorView.getContext(),animatorView);
@@ -743,6 +897,52 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
backGroundAnimator= dotsLineAnimator;
backGroundAnimator.setRandColor(true);
break;
case 20:
if(waterAnimator ==null) {
waterAnimator = new WaterAnimator();
waterAnimator.init(animatorView.getContext(),animatorView);
waterAnimator.setColor(foregroundColor);
}
backGroundAnimator= waterAnimator;
backGroundAnimator.setRandColor(true);
break;
case 21:
if(fireAnimator ==null) {
fireAnimator = new FireAnimator();
fireAnimator.init(animatorView.getContext(),animatorView);
fireAnimator.setColor(foregroundColor);
}
backGroundAnimator= fireAnimator;
//backGroundAnimator.setRandColor(true);
break;
case 22:
if(skyAnimator==null) {
skyAnimator = new SkyAnimator();
skyAnimator.init(animatorView.getContext(),animatorView);
skyAnimator.setColor(foregroundColor);
}
backGroundAnimator=skyAnimator;
backGroundAnimator.setRandColor(false);
break;
case 23:
if(rippleAnimator ==null) {
rippleAnimator = new RippleAnimator();
rippleAnimator.init(animatorView.getContext(),animatorView);
rippleAnimator.setColor(foregroundColor);
}
backGroundAnimator= rippleAnimator;
backGroundAnimator.setRandColor(true);
break;
case 1:
if(windmillAnimator ==null) {
windmillAnimator = new WindmillAnimator();
windmillAnimator.init(animatorView.getContext(),animatorView);
windmillAnimator.setColor(foregroundColor);
}
backGroundAnimator= windmillAnimator;
backGroundAnimator.setRandColor(true);
break;
default:
currectAnimatorIndex=0;
backGroundAnimator=null;
@@ -752,9 +952,13 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
private void upHandStatic(){
if(handUpAbla) {
this.tv_handup.setColorFilter(0x000);
this.tv_handup.setColorFilter(foregroundColor);
//if(hand_time_visable)
hand_time_visable=true;
this.tv_hand_time.setVisibility(View.VISIBLE);
}else{
this.tv_handup.setColorFilter(R.color.colorPrimaryDark);
this.tv_hand_time.setVisibility(View.GONE);
}
}
@@ -795,6 +999,26 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
handUpTimePopup.showPopupWindow();
}
private void setupTempHandUpTime(){
handUpAbla=false;
int hour=0;
int minute=0;
if(handUpTimePopup==null) {
handUpTimePopup = new TimeSetupPopup(this);
handUpTimePopup.setOnSeekBarChangeListener(new TimeSetupPopup.OnTimeChangeListener() {
@Override
public void onChanged(int hour, int minute) {
handUpTime=hour*60+minute;
}
});
}
hour=handUpTime/60;
minute=handUpTime-hour*60;
handUpTimePopup.init(hour,minute);
handUpTimePopup.showPopupWindow();
handUpAbla=true;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(screenLock)
@@ -842,8 +1066,9 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
private int handUPDialy=60;
private void checkHandUp(){
if(!handUpAbla)
if(!handUpAbla) {
return;
}
//Log.d(TAG,"checkHandUp..\thandUPDialy:"+handUPDialy+" \thandUpTime:"+handUpTime+"\thandUpAble:"+handUpAbla);
if(mMode==MODE_HANDUP) {
tv_handup_image.setText("hand up:"+handUPDialy);
@@ -855,6 +1080,8 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
return;
}
handUpTime--;
if(hand_time_visable)
tv_hand_time.setText(DateModel.getTime(handUpTime));
if (handUpTime <= 10&&handUpTime > 0)
setDiscript("提醒时间倒计时: " + handUpTime);
if (handUpTime == 0) {
@@ -864,17 +1091,45 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
}
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
DigitTextView.DigitSpan digitSpan,digitSpan1;
DigitTextView.DigitSmallSpan digitSpan2;
SpannableString spannable;
DigitTextView.DigitFlagSpan digitFlagSpan;
private void setCurrentTimeToView(String timeString){
spannable = new SpannableString(timeString);
if(digitSpan==null) {
digitSpan = new DigitTextView.DigitSpan();
digitSpan1 = new DigitTextView.DigitSpan();
digitSpan2 = new DigitTextView.DigitSmallSpan();
digitFlagSpan=new DigitTextView.DigitFlagSpan();
}
spannable.setSpan(digitSpan,0,2,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(digitFlagSpan,2,3,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(digitSpan1,3,5,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if(timeString.length()>5)
spannable.setSpan(digitSpan2,5,8,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
//tv_time.setText(spannable, TextView.BufferType.SPANNABLE);
tv_time.setText(spannable, TextView.BufferType.NORMAL);
//tv_time.setText(timeString);
}
boolean heartbeat=false;
private void updateTime() {
heartbeat=!heartbeat;
DateModel date = new DateModel();
String timeString = model.isDisplaySecond() ? date.getTimeString() : date.getShortTimeString();
//timeString=timeString.replace("1", " 1");
if(timeString.startsWith(" "))
timeString=timeString.substring(1,timeString.length());
tv_time.setText(timeString);
//tv_date.setText(dateString.replace("1", " 1"));
String timeString = model.isDisplaySecond() ? date.getTimeString(model.isHourSystem12()) : date.getShortTimeString(heartbeat,model.isHourSystem12());
setCurrentTimeToView(timeString);
reportTime(date);
updateDay(date);
updateHourSytemStyle(date);
}
int beforeHourForUpdateSystem=-1;
private void updateHourSytemStyle(DateModel date){
if(model.isHourSystem12()&&beforeHourForUpdateSystem!=date.getHour()) {
beforeHourForUpdateSystem=date.getHour();
tv_hours_system.setImageResource(beforeHourForUpdateSystem>12?R.drawable.ic_pm:R.drawable.ic_am);
}
}
DateModel currentDate=null;
@@ -960,6 +1215,7 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
@Override
public boolean onDown(MotionEvent motionEvent) {
return false;
}
@@ -981,6 +1237,31 @@ public class MainActivity extends Activity implements Handler.Callback, View.OnC
@Override
public void onLongPress(MotionEvent motionEvent) {
if(screenLock&&isFullScreen) {
if(tv_time.getVisibility()==View.GONE) {
tv_time.setVisibility(View.VISIBLE);
isArtificialHiddle=false;
return;
}
if(particleSmasher==null)
particleSmasher=new ParticleSmasher(this);
particleSmasher.with(tv_time).setDuration(1000).addAnimatorListener(new SmashAnimator.OnAnimatorListener() {
@Override
public void onAnimatorStart() {
super.onAnimatorStart();
// 回调,动画开始
}
@Override
public void onAnimatorEnd() {
super.onAnimatorEnd();
// 回调,动画结束
particleSmasher.reShowView(tv_time);
tv_time.setVisibility(View.GONE);
isArtificialHiddle=true;
}
}).startRandomAnimator();
}
if(screenLock)
return;
if(!isPowerManagerDisable())

View File

@@ -90,8 +90,6 @@ public class DateModel implements Serializable {
} else {
setDataString(getNowWithTime());
}
}
public String toString() {
@@ -102,12 +100,30 @@ public class DateModel implements Serializable {
return String.format("%04d-%02d-%02d ", year, month, day);
}
public String getShortTimeString() {
return String.format("%02d:%02d", hour, minute);
}
public String getTimeString() {
return String.format("%02d:%02d:%02d", hour, minute, second);
return String.format("%02d:%02d %02d", hour, minute, second);
}
public String getShortTimeString(boolean heartbeat,boolean isHourSystem12) {
if(heartbeat)
if(isHourSystem12&&hour>12)
return String.format("%02d %02d", hour-12, minute);
else
return String.format("%02d %02d", hour, minute);
if(isHourSystem12&&hour>12)
return String.format("%02d:%02d", hour-12, minute, second);
return String.format("%02d:%02d", hour, minute);
}
public String getTimeString(boolean isHourSystem12) {
if(isHourSystem12&&hour>12)
return String.format("%02d:%02d %02d", hour-12, minute, second);
return String.format("%02d:%02d %02d", hour, minute, second);
}
public static String getNowWithTime() {
@@ -116,6 +132,18 @@ public class DateModel implements Serializable {
return sdf.format(dt);
}
public static String getTime(int second){
long hours = second / 3600;//转换小时数
second = second % 3600;//剩余秒数
long minutes = second / 60;//转换分钟
second = second % 60;//剩余秒数
if(minutes>0||hours>0){
return String.format(" %02d:%02d", hours, minutes, second);
}
return String.format("%02d:%02d:%02d", hours, minutes, second);
}
public int minusTime(DateModel strEnd) {
SimpleDateFormat df = new SimpleDateFormat("HH:mm");
Date d1 = null;

View File

@@ -28,6 +28,9 @@ public class SharePerferenceModel implements Serializable {
private boolean isTriggerScreen = true;
private final static String KEY_IS_TRIGGER_SCREEN = "key_is_trigger_screen";
private boolean hourSystem12 = false;
private final static String KEY_IS_HOUR_SYSTEM_12 = "key_is_hour_system_12";
private final static String KEY_CITY = "key_city";
private String mCity;
@@ -193,6 +196,7 @@ public class SharePerferenceModel implements Serializable {
backgroundColor=jsonObject.optInt(KEY_BACKGROUND_COLOR,Color.rgb(0, 0, 0));
foregroundColor=jsonObject.optInt(KEY_FOREGROUND_COLOR,Color.rgb(255, 255, 255));
foregroundColor1=jsonObject.optInt(KEY_FOREGROUND_COLOR1,Color.rgb(199,21,133));
hourSystem12=jsonObject.optBoolean(KEY_IS_HOUR_SYSTEM_12,false);
} catch (JSONException e) {
e.printStackTrace();
}
@@ -221,6 +225,7 @@ public class SharePerferenceModel implements Serializable {
jsonObject.put(KEY_BACKGROUND_COLOR,backgroundColor);
jsonObject.put(KEY_FOREGROUND_COLOR,foregroundColor);
jsonObject.put(KEY_FOREGROUND_COLOR1,foregroundColor1);
jsonObject.put(KEY_IS_HOUR_SYSTEM_12,hourSystem12);
} catch (JSONException e) {
e.printStackTrace();
}
@@ -260,6 +265,7 @@ public class SharePerferenceModel implements Serializable {
", backgroundColor="+backgroundColor+
", foregroundColor="+foregroundColor+
", foregroundColor1="+foregroundColor1+
", hourSystem12="+hourSystem12+
'}';
}
@@ -311,4 +317,12 @@ public class SharePerferenceModel implements Serializable {
public void setForegroundColor1(Integer foregroundColor1) {
this.foregroundColor1 = foregroundColor1;
}
public boolean isHourSystem12() {
return hourSystem12;
}
public void setHourSystem12(boolean hourSystem12) {
this.hourSystem12 = hourSystem12;
}
}

View File

@@ -0,0 +1,331 @@
package clock.socoolby.com.clock.widget.animatorview.animator;
import android.graphics.Canvas;
import android.graphics.Paint;
import java.util.Calendar;
import clock.socoolby.com.clock.widget.animatorview.AbstractAnimator;
// 参考自:https://github.com/ifadai/ClockDemo
public class ClockAnimator extends AbstractAnimator<ClockAnimator.Clock> {
private final String TAG = getClass().getSimpleName();
@Override
public boolean run() {
for(Clock clock:list)
clock.getTime();
return true;
}
@Override
public void onDraw(Canvas canvas) {
for(Clock clock:list)
clock.onDraw(canvas);
}
@Override
public void reset() {
super.reset();
int textColor=color;
if(randColor)
randomColor();
int minHigh=Math.min(width,height);
int r=rand.nextInt(minHigh);
if(r<minHigh/2)
r=minHigh/2;
list.add(new Clock(width/2,height/2,r,color,textColor));
}
public class Clock{
private int mCenterX, mCenterY;
// 默认刻度画笔、指针画笔、文字画笔;
private Paint mDefaultPaint, mPointerPaint, mTextPaint;
// 时钟半径、中心点半径、默认刻度长度、默认刻度宽度、特殊刻度长度、特殊刻度宽度、
// 时指针长度、时钟指针宽度、分钟指针长度、分钟指针宽度、秒钟指针长度、秒钟指针宽度
private float mRadius, mPointRadius,
mDefaultScaleLength, mDefaultScaleWidth,
mParticularlyScaleLength, mParticularlyScaleWidth,
mHourPointerLength, mHourPointerWidth,
mMinutePointerLength, mMinutePointerWidth,
mSecondPointerLength, mSecondPointerWidth;
// 当前时、分、秒
private int mH, mM, mS;
// 时钟颜色、默认刻度颜色、时刻度颜色、时针颜色、分针颜色、秒针颜色
private int mClockColor, mColorDefaultScale, mColorParticularyScale, mColorHourPointer,
mColorMinutePointer, mColorSecondPointer,textColor;
public Clock(int mCenterX, int mCenterY, float mRadius, int mClockColor,int textColor) {
this.mCenterX = mCenterX;
this.mCenterY = mCenterY;
this.mRadius = mRadius;
this.mClockColor = mClockColor;
this.textColor=textColor;
init();
}
private void init() {
mColorDefaultScale = mClockColor;
mColorParticularyScale = mClockColor;
mColorHourPointer = mClockColor;
mColorMinutePointer = mClockColor;
mColorSecondPointer = mClockColor;
mDefaultPaint = new Paint();
mDefaultPaint.setAntiAlias(true);
mDefaultPaint.setStyle(Paint.Style.STROKE);
mPointerPaint = new Paint();
mPointerPaint.setAntiAlias(true);
mPointerPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPointerPaint.setTextSize(14);
mPointerPaint.setStrokeCap(Paint.Cap.ROUND);
mTextPaint = new Paint();
mPointerPaint.setAntiAlias(true);
mPointerPaint.setStyle(Paint.Style.FILL);
mPointerPaint.setColor(mClockColor);
initClockPointerLength();
}
/**
* 获取当前系统时间
*/
private void getTime() {
Calendar calendar = Calendar.getInstance();
int hour = calendar.get(Calendar.HOUR);
hour = hour % 12;
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
if (hour != mH || minute != mM || second != mS) {
setTime(hour, minute, second);
}
}
/**
* 设置时间
*/
private void setTime(int h, int m, int s) {
mH = h;
mM = m;
mS = s;
}
/**
* 根据控件的大小,初始化时钟刻度的长度和宽度、指针的长度和宽度、时钟中心点的半径
*/
private void initClockPointerLength() {
/*
* 默认时钟刻度长=半径/10;
* 默认时钟刻度宽=长/6;
*
* */
mDefaultScaleLength = mRadius / 10;
mDefaultScaleWidth = mDefaultScaleLength / 6;
/*
* 特殊时钟刻度长=半径/5;
* 特殊时钟刻度宽=长/6;
*
* */
mParticularlyScaleLength = mRadius / 5;
mParticularlyScaleWidth = mParticularlyScaleLength / 6;
/*
* 时针长=半径/3;
* 时针宽=特殊时钟刻度宽;
*
* */
mHourPointerLength = mRadius / 3;
mHourPointerWidth = mParticularlyScaleWidth;
/*
* 分针长=半径/2;
* 分针宽=特殊时钟刻度宽;
*
* */
mMinutePointerLength = mRadius / 2;
mMinutePointerWidth = mParticularlyScaleWidth;
/*
* 秒针长=半径/3*2;
* 秒针宽=默认时钟刻度宽;
*
* */
mSecondPointerLength = mRadius / 3 * 2;
mSecondPointerWidth = mDefaultScaleWidth;
// 中心点半径=(默认刻度宽+特殊刻度宽)/2
mPointRadius = (mDefaultScaleWidth + mParticularlyScaleWidth) / 2;
}
protected void onDraw(Canvas canvas) {
// 坐标原点移动到View 中心
canvas.translate(mCenterX, mCenterY);
drawCircle(canvas);
drawText(canvas);
drawPointer(canvas);
}
/**
* 绘制时钟的圆形和刻度
*/
private void drawCircle(Canvas canvas) {
mDefaultPaint.setStrokeWidth(mDefaultScaleWidth);
mDefaultPaint.setColor(mClockColor);
canvas.drawCircle(0, 0, mRadius, mDefaultPaint);
for (int i = 0; i < 60; i++) {
if (i % 5 == 0) { // 特殊时刻
mDefaultPaint.setStrokeWidth(mParticularlyScaleWidth);
mDefaultPaint.setColor(mColorParticularyScale);
canvas.drawLine(0, -mRadius, 0, -mRadius + mParticularlyScaleLength, mDefaultPaint);
} else { // 一般时刻
mDefaultPaint.setStrokeWidth(mDefaultScaleWidth);
mDefaultPaint.setColor(mColorDefaultScale);
canvas.drawLine(0, -mRadius, 0, -mRadius + mDefaultScaleLength, mDefaultPaint);
}
canvas.rotate(6);
}
}
/**
* 绘制特殊时刻12点、3点、6点、9点)的文字
*/
private void drawText(Canvas canvas) {
setTextPaint();
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
// 文字顶部与基线距离
float ascent = Math.abs(fontMetrics.ascent);
// 文字底部与基线距离
float descent = Math.abs(fontMetrics.descent);
// 文字高度
float fontHeight = ascent + descent;
// 文字竖直中心点距离基线的距离;
float offsetY = fontHeight / 2 - Math.abs(fontMetrics.descent);
// 文字宽度
float fontWidth;
// drawText(@NonNull String text, float x, float y, @NonNull Paint paint) 参数y为基线的y坐标并非文字左下角的坐标
// 文字距离圆圈的距离为 特殊刻度长度+宽度
String h = "12";
// y轴坐标为: -(半径-特殊刻度长度-特殊刻度宽度(作为间距)-文字顶部距离基线的距离)
float y = -(mRadius - mParticularlyScaleLength - mParticularlyScaleWidth - ascent);
canvas.drawText(h, 0, y, mTextPaint);
h = "3";
fontWidth = mTextPaint.measureText(h);
// y轴坐标为: 半径-特殊刻度长度-特殊刻度宽度(作为间距)-文字长度/2绘制原点在文字横向中心
y = mRadius - mParticularlyScaleLength - mParticularlyScaleWidth - (fontWidth / 2);
canvas.drawText(h, y, 0 + offsetY, mTextPaint);
h = "6";
// y轴坐标为: 半径-特殊刻度长度-特殊刻度宽度(作为间距)-文字底部与基线的距离
y = mRadius - mParticularlyScaleLength - mParticularlyScaleWidth - descent;
canvas.drawText(h, 0, y, mTextPaint);
h = "9";
fontWidth = mTextPaint.measureText(h);
// y轴坐标为: -(半径-特殊刻度长度-特殊刻度宽度(作为间距)-文字长度/2绘制原点在文字横向中心
y = -(mRadius - mParticularlyScaleLength - mParticularlyScaleWidth - (fontWidth / 2));
canvas.drawText(h, y, 0 + offsetY, mTextPaint);
}
private void setTextPaint() {
mTextPaint.setStrokeWidth(mDefaultScaleWidth / 2);
mTextPaint.setTextSize(mParticularlyScaleWidth * 4);
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setColor(textColor);
}
/**
* 绘制指针
*/
private void drawPointer(Canvas canvas) {
drawHourPointer(canvas);
drawMinutePointer(canvas);
drawSecondPointer(canvas);
mPointerPaint.setColor(mClockColor);
// 绘制中心原点,需要在指针绘制完成后才能绘制
canvas.drawCircle(0, 0, mPointRadius, mPointerPaint);
}
/**
* 绘制时针
*/
private void drawHourPointer(Canvas canvas) {
mPointerPaint.setStrokeWidth(mHourPointerWidth);
mPointerPaint.setColor(mColorHourPointer);
// 当前时间的总秒数
float s = mH * 60 * 60 + mM * 60 + mS;
// 百分比
float percentage = s / (12 * 60 * 60);
// 通过角度计算弧度值因为时钟的角度起线是y轴负方向而View角度的起线是x轴正方向所以要加270度
float angle = 270 + 360 * percentage;
float x = (float) (mHourPointerLength * Math.cos(Math.PI * 2 / 360 * angle));
float y = (float) (mHourPointerLength * Math.sin(Math.PI * 2 / 360 * angle));
canvas.drawLine(0, 0, x, y, mPointerPaint);
}
/**
* 绘制分针
*/
private void drawMinutePointer(Canvas canvas) {
mPointerPaint.setStrokeWidth(mMinutePointerWidth);
mPointerPaint.setColor(mColorMinutePointer);
float s = mM * 60 + mS;
float percentage = s / (60 * 60);
float angle = 270 + 360 * percentage;
float x = (float) (mMinutePointerLength * Math.cos(Math.PI * 2 / 360 * angle));
float y = (float) (mMinutePointerLength * Math.sin(Math.PI * 2 / 360 * angle));
canvas.drawLine(0, 0, x, y, mPointerPaint);
}
/**
* 绘制秒针
*/
private void drawSecondPointer(Canvas canvas) {
mPointerPaint.setStrokeWidth(mSecondPointerWidth);
mPointerPaint.setColor(mColorSecondPointer);
float s = mS;
float percentage = s / 60;
float angle = 270 + 360 * percentage;
float x = (float) (mSecondPointerLength * Math.cos(Math.PI * 2 / 360 * angle));
float y = (float) (mSecondPointerLength * Math.sin(Math.PI * 2 / 360 * angle));
canvas.drawLine(0, 0, x, y, mPointerPaint);
}
}
}

View File

@@ -0,0 +1,182 @@
package clock.socoolby.com.clock.widget.animatorview.animator;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import java.util.ArrayList;
import java.util.List;
import clock.socoolby.com.clock.widget.animatorview.AbstractCacheDifferenceAnimator;
//参考: https://www.jianshu.com/p/0d4ab16a6b7b
public class FireAnimator extends AbstractCacheDifferenceAnimator<FireAnimator.ParticleSystem, FireAnimator.Particle> {
@Override
public void reset() {
super.reset();
addFires(1);
}
int high=0;
int fireWidth=0;
int particlesCount;
public void addFires(int quantity) {
for (int i = 0; i < quantity;i++) {
high=rand.nextInt(height);
if(high<=height/3)
high=high+height/2;
fireWidth=rand.nextInt(width/2);
particlesCount=rand.nextInt(300);
particlesCount=particlesCount>150?particlesCount:particlesCount+150;
list.add(new ParticleSystem(width/2,height,high,fireWidth,particlesCount,rand.nextInt(30)));
}
}
@Override
public boolean run() {
for(ParticleSystem fire:list)
fire.update(WIND.NULL,0);
//fire.update(rand.nextFloat()*0.18f);
return true;
}
@Override
public void onDraw(Canvas canvas) {
for(ParticleSystem fire:list)
fire.onDraw(canvas);
}
public class ParticleSystem {
private List<Particle> particles = new ArrayList<>();
private int fire_width = 20;
private int particle_width=20;
private Paint paint = new Paint();
private int x,y,high;
public ParticleSystem(int x,int y,int high,int fire_width,int particlesCount,int particle_width) {
this.x=x;
this.y=y;
this.high=high;
this.fire_width =fire_width;
this.particle_width=particle_width;
if(particle_width<10)
this.particle_width=10;
if(randColor) {
randomColor();
paint.setColor(color);
}else
paint.setColor(Color.RED);
for (int i = 0; i < particlesCount; i++) {
particles.add(newParticle());
}
}
public void onDraw(Canvas canvas) {
for (int i = 0; i < particles.size(); i++) {
Particle particle = particles.get(i);
paint.setAlpha(particle.alpha);
canvas.drawRect(particle.getX(), particle.getY(), particle.getX() + particle_width, particle.getY() + particle_width, paint);
}
}
/**
* 粒子集合更新方法
*
* @param wind wind是风volume 是风加速度 0时代表无风粒子不偏移
*/
public void update(WIND wind,float volume) {
for (int i = 0; i < particles.size(); i++) {
Particle particle = particles.get(i);
if (particle.xv > 0) {
//当初始加速度为正,就一直为正
particle.xv += 0.01f + volume;
} else {
//当初始加速度为负,就一直为正
particle.xv += -0.01f + volume;
}
particle.yv = particle.yv + 0.1f;
switch (wind){
case LEFT_TO_RIGHT:
break;
case RIGHT_TO_LEFT:
break;
}
particle.setX(particle.getX() + particle.xv);
particle.setY(particle.getY() - particle.yv);
//跟据上升高度 计算透明度
particle.alpha = 255-(int) ((y-particle.y)*255/high);
}
List<Particle> list = new ArrayList<>();
list.addAll(particles);
for (Particle particle : list) {
if (particle.getY() < y-high) {
moveToTrashCache(particle);
particles.remove(particle);
}
}
for (int i = 0; i < 5; i++) {
particles.add(newParticle());
}
}
public Particle newParticle(){
Particle particle =revectForTrashCache();
if(particle==null)
particle = new Particle();
if(rand.nextBoolean())
particle.x=x - rand.nextInt(fire_width /2);
else
particle.x=x + rand.nextInt(fire_width /2);
particle.y=y;
particle.xv = 0.5f - 1 * rand.nextFloat();
particle.yv=1;
particle.alpha=255;
return particle;
}
}
//用于两边向中间靠拢
public enum WIND{
LEFT_TO_RIGHT,NULL,RIGHT_TO_LEFT,
TORNADO;//龙卷风
}
public class Particle {
//火焰粒子坐标
private float x;
private float y;
public float xv = 1;//x轴加速度
public float yv = 1;//y轴加速度
public int alpha;
public Particle(){
}
public Particle(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
}
}

View File

@@ -0,0 +1,381 @@
package clock.socoolby.com.clock.widget.animatorview.animator;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.PointF;
//参考自https://blog.csdn.net/guimianhao9833/article/details/75094010
public class FishAnimator {
private static final String TAG = "Jcs_Fishsss";
private static final float HEAD_RADIUS = 50;
protected static final float BODY_LENGHT = HEAD_RADIUS * 3.2f; //第一节身体长度
private static final int BODY_ALPHA = 220;
private static final int OTHER_ALPHA = 160;
private static final int FINS_ALPHA = 100;
private static final int FINS_LEFT = 1;//左鱼鳍
private static final int FINS_RIGHT = -1;
private static final float FINS_LENGTH = HEAD_RADIUS * 1.3f;
public static final float TOTAL_LENGTH = 6.79f * HEAD_RADIUS;
public class Fish{
private Paint mPaint;
//控制区域
private int currentValue = 0;//全局控制标志
private float mainAngle = 90;//角度表示的角
private float waveFrequence = 1;
//鱼头点
private PointF headPoint;
//转弯更自然的中心点
private PointF middlePoint;
private float finsAngle = 0;
private Paint bodyPaint;
private Path mPath;
public Fish() {
init();
}
public void run(){
//currentValue = (int) (animation.getAnimatedValue());
//invalidateSelf();
}
private void init() {
//路径
mPath = new Path();
//画笔
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setDither(true);//防抖
mPaint.setColor(Color.argb(OTHER_ALPHA, 244, 92, 71));
//身体画笔
bodyPaint = new Paint();
bodyPaint.setAntiAlias(true);
bodyPaint.setStyle(Paint.Style.FILL);
bodyPaint.setDither(true);//防抖
bodyPaint.setColor(Color.argb(OTHER_ALPHA + 5, 244, 92, 71));
middlePoint = new PointF(4.18f * HEAD_RADIUS, 4.18f * HEAD_RADIUS);
//鱼鳍灵动动画
//finsAnimator = ObjectAnimator.ofFloat(this, "finsAngle", 0f, 1f, 0f);
//finsAnimator.setRepeatMode(ValueAnimator.REVERSE);
//finsAnimator.setRepeatCount(new Random().nextInt(3));
//引擎部分
//ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 540 * 100);
//valueAnimator.setDuration(180 * 1000);
//valueAnimator.setInterpolator(new LinearInterpolator());
//valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
//valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
//valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
// @Override
// public void onAnimationUpdate(ValueAnimator animation) {
// currentValue = (int) (animation.getAnimatedValue());
// invalidateSelf();
// }
//});
//valueAnimator.addListener(new AnimatorListenerAdapter() {
// @Override
// public void onAnimationRepeat(Animator animation) {
// super.onAnimationRepeat(animation);
// finsAnimator.start();
// }
//});
//valueAnimator.start();
}
public PointF getHeadPoint() {
return headPoint;
}
/**
* 设置身体主轴线方向角度
*
* @param mainAngle
*/
public void setMainAngle(float mainAngle) {
this.mainAngle = mainAngle;
}
/**
* 获取当前角度
*
* @return
*/
public float getMainAngle() {
return mainAngle;
}
/**
* 设置头的位置
*
* @param headPoint
*/
public void setHeadPoint(PointF headPoint) {
this.headPoint = headPoint;
}
public void setMiddlePoint(PointF middlePoint) {
this.middlePoint = middlePoint;
}
public PointF getMiddlePoint() {
return middlePoint;
}
public float getTotalLength() {
return TOTAL_LENGTH;
}
public void draw(Canvas canvas) {
//生成一个半透明图层,否则与背景白色形成干扰,尺寸必须与view的大小一致否则鱼显示不全
canvas.saveLayerAlpha(0, 0, canvas.getWidth(), canvas.getHeight(), 240, Canvas.ALL_SAVE_FLAG);
makeBody(canvas, HEAD_RADIUS);
canvas.restore();
mPath.reset();
mPaint.setColor(Color.argb(OTHER_ALPHA, 244, 92, 71));
}
//画身子
/**
* 主方向是头到尾的方向跟X轴正方向的夹角顺时针为正
* 前进方向和主方向相差180度
* R + 3.2R
*
* @param canvas
* @param headRadius
*/
private void makeBody(Canvas canvas, float headRadius) {
//sin参数为弧度值
//现有角度=原始角度+ sin域值[-11]*可摆动的角度 sin作用是控制周期摆动
float angle = mainAngle + (float) Math.sin(Math.toRadians(currentValue * 1.2 * waveFrequence)) * 2;//中心轴线加偏移量和X轴顺时针方向夹角
headPoint = calculatPoint(middlePoint, BODY_LENGHT / 2,mainAngle);
//画头
canvas.drawCircle(headPoint.x, headPoint.y, HEAD_RADIUS, mPaint);
//右鳍 起点
PointF pointFinsRight = calculatPoint(headPoint, headRadius * 0.9f, angle -110);
makeFins(canvas, pointFinsRight, FINS_RIGHT, angle);
//左鳍 起点
PointF pointFinsLeft = calculatPoint(headPoint, headRadius * 0.9f, angle +110);
makeFins(canvas, pointFinsLeft, FINS_LEFT, angle);
PointF endPoint = calculatPoint(headPoint, BODY_LENGHT, angle-180);
//躯干2
PointF mainPoint = new PointF(endPoint.x, endPoint.y);
makeSegments(canvas, mainPoint, headRadius * 0.7f, 0.6f, angle);
PointF point1, point2, point3, point4, contralLeft, contralRight;
//point1和4的初始角度决定发髻线的高低值越大越低
point1 = calculatPoint(headPoint, headRadius, angle-80);
point2 = calculatPoint(endPoint, headRadius * 0.7f, angle-90);
point3 = calculatPoint(endPoint, headRadius * 0.7f, angle +90);
point4 = calculatPoint(headPoint, headRadius, angle +80);
//决定胖瘦
contralLeft = calculatPoint(headPoint, BODY_LENGHT * 0.56f, angle -130);
contralRight = calculatPoint(headPoint, BODY_LENGHT * 0.56f, angle +130);
mPath.reset();
mPath.moveTo(point1.x, point1.y);
mPath.quadTo(contralLeft.x, contralLeft.y, point2.x, point2.y);
mPath.lineTo(point3.x, point3.y);
mPath.quadTo(contralRight.x, contralRight.y, point4.x, point4.y);
mPath.lineTo(point1.x, point1.y);
mPaint.setColor(Color.argb(BODY_ALPHA, 244, 92, 71));
//画最大的身子
canvas.drawPath(mPath, mPaint);
}
/**
* 第二节节肢
* 0.7R * 0.6 =1.12R
*
* @param canvas
* @param mainPoint
* @param segmentRadius
* @param MP 梯形上边下边长度比
*/
private void makeSegments(Canvas canvas, PointF mainPoint, float segmentRadius, float MP, float fatherAngle) {
float angle = fatherAngle + (float) Math.cos(Math.toRadians(currentValue * 1.5 * waveFrequence)) * 15;//中心轴线和X轴顺时针方向夹角
//身长
float segementLenght = segmentRadius * (MP + 1);
PointF endPoint = calculatPoint(mainPoint, segementLenght, angle-180);
PointF point1, point2, point3, point4;
point1 = calculatPoint(mainPoint, segmentRadius, angle-90);
point2 = calculatPoint(endPoint, segmentRadius * MP,angle-90);
point3 = calculatPoint(endPoint, segmentRadius * MP, angle +90);
point4 = calculatPoint(mainPoint, segmentRadius, angle+90);
canvas.drawCircle(mainPoint.x, mainPoint.y, segmentRadius, mPaint);
canvas.drawCircle(endPoint.x, endPoint.y, segmentRadius * MP, mPaint);
mPath.reset();
mPath.moveTo(point1.x, point1.y);
mPath.lineTo(point2.x, point2.y);
mPath.lineTo(point3.x, point3.y);
mPath.lineTo(point4.x, point4.y);
canvas.drawPath(mPath, mPaint);
//躯干2
PointF mainPoint2 = new PointF(endPoint.x, endPoint.y);
makeSegmentsLong(canvas, mainPoint2, segmentRadius * 0.6f, 0.4f, angle);
}
/**
* 第三节节肢
* 0.7R * 0.6 * (0.4 + 2.7) + 0.7R * 0.6 * 0.4=1.302R + 0.168R
*
* @param canvas
* @param mainPoint
* @param segmentRadius
* @param MP 梯形上边下边长度比
*/
private void makeSegmentsLong(Canvas canvas, PointF mainPoint, float segmentRadius, float MP, float fatherAngle) {
float angle = fatherAngle + (float) Math.sin(Math.toRadians(currentValue * 1.5 * waveFrequence)) * 35;//中心轴线和X轴顺时针方向夹角
//身长
float segementLenght = segmentRadius * (MP + 2.7f);
PointF endPoint = calculatPoint(mainPoint, segementLenght, angle-180);
PointF point1, point2, point3, point4;
point1 = calculatPoint(mainPoint, segmentRadius, angle -90 );
point2 = calculatPoint(endPoint, segmentRadius * MP,angle -90 );
point3 = calculatPoint(endPoint, segmentRadius * MP, angle +90);
point4 = calculatPoint(mainPoint, segmentRadius, angle +90);
makeTail(canvas, mainPoint, segementLenght, segmentRadius, angle);
canvas.drawCircle(endPoint.x, endPoint.y, segmentRadius * MP, mPaint);
mPath.reset();
mPath.moveTo(point1.x, point1.y);
mPath.lineTo(point2.x, point2.y);
mPath.lineTo(point3.x, point3.y);
mPath.lineTo(point4.x, point4.y);
canvas.drawPath(mPath, mPaint);
}
/**
* 鱼鳍
*
* @param canvas
* @param startPoint
* @param type
*/
private void makeFins(Canvas canvas, PointF startPoint, int type, float fatherAngle) {
float contralAngle = 115;//鱼鳍三角控制角度
mPath.reset();
mPath.moveTo(startPoint.x, startPoint.y);
PointF endPoint = calculatPoint(startPoint, FINS_LENGTH, type == FINS_RIGHT ? fatherAngle - finsAngle-180 : fatherAngle + finsAngle+180);
PointF contralPoint = calculatPoint(startPoint, FINS_LENGTH * 1.8f, type == FINS_RIGHT ?
fatherAngle - contralAngle - finsAngle : fatherAngle + contralAngle + finsAngle);
mPath.quadTo(contralPoint.x, contralPoint.y, endPoint.x, endPoint.y);
mPath.lineTo(startPoint.x, startPoint.y);
mPaint.setColor(Color.argb(FINS_ALPHA, 244, 92, 71));
canvas.drawPath(mPath, mPaint);
mPaint.setColor(Color.argb(OTHER_ALPHA, 244, 92, 71));
}
/**
* 鱼尾及鱼尾张合
*
* @param canvas
* @param mainPoint
* @param length
* @param maxWidth
*/
private void makeTail(Canvas canvas, PointF mainPoint, float length, float maxWidth, float angle) {
float newWidth = (float) Math.abs(Math.sin(Math.toRadians(currentValue * 1.7 * waveFrequence)) * maxWidth + HEAD_RADIUS/5*3);
//endPoint为三角形底边中点
PointF endPoint = calculatPoint(mainPoint, length, angle-180);
PointF endPoint2 = calculatPoint(mainPoint, length - 10, angle-180);
PointF point1, point2, point3, point4;
point1 = calculatPoint(endPoint, newWidth, angle-90);
point2 = calculatPoint(endPoint, newWidth, angle +90);
point3 = calculatPoint(endPoint2, newWidth - 20, angle-90);
point4 = calculatPoint(endPoint2, newWidth - 20, angle +90);
//内
mPath.reset();
mPath.moveTo(mainPoint.x, mainPoint.y);
mPath.lineTo(point3.x, point3.y);
mPath.lineTo(point4.x, point4.y);
mPath.lineTo(mainPoint.x, mainPoint.y);
canvas.drawPath(mPath, mPaint);
//外
mPath.reset();
mPath.moveTo(mainPoint.x, mainPoint.y);
mPath.lineTo(point1.x, point1.y);
mPath.lineTo(point2.x, point2.y);
mPath.lineTo(mainPoint.x, mainPoint.y);
canvas.drawPath(mPath, mPaint);
}
private void setFinsAngle(float currentValue) {
finsAngle = 45 * currentValue;
}
public void setWaveFrequence(float waveFrequence) {
this.waveFrequence = waveFrequence;
}
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
/**
* 高度要容得下两个鱼身长度
* 8.36计算过程 身长6.79减去头顶到中部位置的长度2.6 再乘以2
*
* @return
*/
public int getIntrinsicHeight() {
return (int) (8.38f * HEAD_RADIUS);
}
public int getIntrinsicWidth() {
return (int) (8.38f * HEAD_RADIUS);
}
}
/**
* 输入起点、长度、旋转角度计算终点
* @param startPoint 起点
* @param length 长度
* @param angle 旋转角度
* @return 计算结果点
*/
private static PointF calculatPoint(PointF startPoint, float length, float angle) {
float deltaX = (float) Math.cos(Math.toRadians(angle)) * length;
//符合Android坐标的y轴朝下的标准
float deltaY = (float) Math.sin(Math.toRadians(angle-180)) * length;
return new PointF(startPoint.x + deltaX, startPoint.y + deltaY);
}
}

View File

@@ -0,0 +1,345 @@
package clock.socoolby.com.clock.widget.animatorview.animator;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import java.util.ArrayList;
import java.util.List;
import clock.socoolby.com.clock.widget.animatorview.AbstractAnimator;
//参考自:https://github.com/zhuwentao2150/RippleView
public class RippleAnimator extends AbstractAnimator<RippleAnimator.Ripple> {
@Override
public void reset() {
super.reset();
Style style;
int ran=rand.nextInt(3);
if(ran==2)
style=Style.OUT;
else if(ran==1)
style=Style.BREATH;
else
style=Style.IN;
list.add(new Ripple(width,height,Color.BLUE,style));
}
@Override
public boolean run() {
for(Ripple ripple:list)
ripple.breath();
return true;
}
@Override
public void onDraw(Canvas canvas) {
for(Ripple ripple:list)
ripple.onDraw(canvas);
}
public enum Style{
IN,OUT,BREATH;
}
public class Ripple{
// 画笔对象
private Paint mPaint;
// View宽
private float mWidth;
// View高
private float mHeight;
// 声波的圆圈集合
private List<Circle> mRipples;
private int sqrtNumber;
// 圆圈扩散的速度
private int mSpeed;
// 圆圈之间的密度
private int mDensity;
// 圆圈的颜色
private int mColor;
// 圆圈是否为填充模式
private boolean mIsFill;
// 圆圈是否为渐变模式
private boolean mIsAlpha;
Style style;
public Ripple(float mWidth, float mHeight, int mColor,Style style) {
this.mWidth = mWidth;
this.mHeight = mHeight;
this.mColor = mColor;
this.style=style;
init();
}
private void init() {
mSpeed = 1;
mDensity = 10;
mIsFill = true;
mIsAlpha = true;
// 设置画笔样式
mPaint = new Paint();
mPaint.setColor(mColor);
mPaint.setStrokeWidth(Util.dipToPX(context,1));
if (mIsFill) {
mPaint.setStyle(Paint.Style.FILL);
} else {
mPaint.setStyle(Paint.Style.STROKE);
}
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setAntiAlias(true);
// 添加第一个圆圈
mRipples = new ArrayList<>();
addNewCircle();
mDensity = Util.dipToPX(context, mDensity);
}
public void onDraw(Canvas canvas) {
switch (style){
case BREATH:
//呼吸的
drawBreath(canvas);
break;
case OUT:
// 外切正方形
drawOutCircle(canvas);
break;
default:
// 内切正方形
drawInCircle(canvas);
}
}
private void addNewCircle(){
Circle c = new Circle(0, 255);
if(randColor) {
randomColor();
c.color=color;
}else
c.color=mColor;
mRipples.add(c);
}
/**
* 圆到宽度
*
* @param canvas
*/
private void drawInCircle(Canvas canvas) {
canvas.save();
// 处理每个圆的宽度和透明度
for (int i = 0; i < mRipples.size(); i++) {
Circle c = mRipples.get(i);
mPaint.setAlpha(c.alpha);// 透明0~255不透明
canvas.drawCircle(mWidth / 2, height / 2, c.width - mPaint.getStrokeWidth(), mPaint);
// 当圆超出View的宽度后删除
if (c.width > width / 2) {
mRipples.remove(i);
} else {
// 计算不透明的数值这里有个小知识就是如果不加上double的话255除以一个任意比它大的数都将是0
if (mIsAlpha) {
double alpha = 255 - c.width * (255 / ((double) mWidth / 2));
c.alpha = (int) alpha;
}
// 修改这个值控制速度
c.width += mSpeed;
}
}
// 里面添加圆
if (mRipples.size() > 0) {
// 控制第二个圆出来的间距
if (mRipples.get(mRipples.size() - 1).width > Util.dipToPX(context, mDensity)) {
addNewCircle();
}
}
canvas.restore();
}
/**
* 圆到对角线
*
* @param canvas
*/
private void drawOutCircle(Canvas canvas) {
canvas.save();
// 使用勾股定律求得一个外切正方形中心点离角的距离
sqrtNumber = (int) (Math.sqrt(mWidth * mWidth + mHeight * mHeight) / 2);
// 变大
for (int i = 0; i < mRipples.size(); i++) {
// 启动圆圈
Circle c = mRipples.get(i);
mPaint.setAlpha(c.alpha);// 透明0~255不透明
canvas.drawCircle(mWidth / 2, mHeight / 2, c.width - mPaint.getStrokeWidth(), mPaint);
// 当圆超出对角线后删掉
if (c.width > sqrtNumber) {
mRipples.remove(i);
} else {
// 计算不透明的度数
double degree = 255 - c.width * (255 / (double) sqrtNumber);
c.alpha = (int) degree;
c.width += 1;
}
}
// 里面添加圆
if (mRipples.size() > 0) {
// 控制第二个圆出来的间距
if (mRipples.get(mRipples.size() - 1).width == 50) {
addNewCircle();
}
}
canvas.restore();
}
private float breathDirection = 1;//呼吸方向(+1:变亮,-1:变暗)
private float breathSpeed = 0.02f;//呼吸速度
private boolean isBreathing = false;
private int a = 0;
private float breath = 1f;//控制呼吸
private int waveCount = 5;//波纹的总个数
public void drawBreath(Canvas canvas) {
int alphaSpeed;
float radiusSpeed;
float hw = mWidth/2f;
/**
* 根据view的宽度计算半径和透明度的变化速度为了尽力保证半径和透明度同时达到最值255是透明度的最大值
*/
if(hw >255f){
radiusSpeed = hw/255f;
alphaSpeed = 1;
}else {
alphaSpeed = new Float(255f/hw).intValue();
radiusSpeed = 1;
}
/**
* 控制呼吸
*/
if(isBreathing){
breath=breath+breathSpeed*breathDirection;
if(breath>1 ){
breathDirection *= -1;//呼吸反向
if(beforBreathIsAwave){
isBreathing = false;
isStarting = true;
breath = 1;
}
}else if(breath <0.001){
breathDirection *= -1;//呼吸反向
if(!beforBreathIsAwave){
isBreathing = false;
isStarting = false;
breath = 1;
}
}
}
/**
* 当达到设定的波距或第一次运行时 添加一个新波
*/
if (++a>= (hw/waveCount) || mRipples.size()<1){
a = 0;
addNewCircle();
}
for (int i= 0;i< mRipples.size();i++){
Circle temp = mRipples.get(i);
if(isStarting){
temp.alpha=temp.alpha-alphaSpeed;//改变波的透明度
if(temp.alpha <0){
temp.alpha=0;
}
temp.width=temp.width + new Float(radiusSpeed).intValue();//增加波的半径
}
mPaint.setColor(temp.color);
int tempAlpha = (int)(temp.alpha*breath);//乘以breath是为了通过改变透明度达到呼吸的效果
mPaint.setAlpha(tempAlpha<0?0:tempAlpha);
canvas.drawCircle(mWidth / 2, mHeight / 2, temp.width, mPaint);//绘制波
/**
* 当波的半径大于本控件的宽大时删除这个波
*/
if( temp.width >mWidth || temp.alpha <0){
mRipples.remove(temp);
}
}
}
private boolean beforBreathIsAwave = true;
private boolean isStarting = true;
private int sleep=0;
/**
* 此方法调用一次波会呼吸一次
*/
public synchronized void breath(){
sleep++;
if(sleep<10)
return;
sleep=0;
this.beforBreathIsAwave = isStarting;
if(beforBreathIsAwave){
breath = 1;
breathDirection =-1;
}
else {
breath = 0.01f;
breathDirection = 1;
}
isBreathing = true;
}
}
class Circle {
Circle(int width, int alpha) {
this.width = width;
this.alpha = alpha;
}
int width;
int alpha;
int color;
}
}

View File

@@ -0,0 +1,256 @@
package clock.socoolby.com.clock.widget.animatorview.animator;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import clock.socoolby.com.clock.widget.animatorview.AbstractAnimator;
public class WaterAnimator extends AbstractAnimator<WaterAnimator.Water> {
@Override
public boolean run() {
for(Water water:list) {
water.runWave();
}
return true;
}
@Override
public void onDraw(Canvas canvas) {
for(Water water:list) {
water.drawWaterView(canvas);
}
}
@Override
public void reset() {
super.reset();
addWater(1);
}
Water water;
public void addWater(int quantity) {
for (int i = 0; i < quantity;i++) {
if(randColor)
randomColor();
Water water=new Water(color,50);
list.add(water);
}
}
public class Water{
private int mWaterWidth;//水波纹圆形的宽度
private int mWaterHeight;//水波纹圆形的高度
private int mWaveCount = 2;//水波纹的个数
private int mStep = 0;//移步的步长
private float mWaterRange = 30;//水波纹的高度
private int mProgressY;//水波纹的中心的Y轴
//private int mRadius;//水波纹圆的半径
private int mWaveLength;//波纹的宽度
private Path path;
private int waterColor;//水波纹的颜色
private Paint waterPaint;
private int mPercent = 0;//当前进度0-100决定Y点位置
public Water(int waterColor,int mPercent) {
this.waterColor = waterColor;
this.mPercent=mPercent;
initWaveRange();
}
/**
* 初始化水波纹的振幅
*/
public void initWaveRange() {
//mHeight = Math.min(width, height);
mWaterWidth = Math.min(width, height);
mWaterHeight = Math.min(width, height);
waterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
waterPaint.setAntiAlias(true);
waterPaint.setColor(waterColor);
waterPaint.setStrokeWidth(3);
waterPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mStep=rand.nextInt(width);//移动步长
mWaterRange = rand.nextInt(200);//高度
if(mWaterRange<10)
mWaterRange=50;
mWaveCount=1;//波浪数
path = new Path();
mWaveLength = mWaterWidth / mWaveCount;//每个波浪数长度
}
/**
* 绘制水波纹
*
* @param canvas
*
*/
public void drawWaterView(Canvas canvas) {
mProgressY = mWaterHeight - (mWaterHeight * mPercent / 100);
//从左到右
canvas.drawPath(getPath(), waterPaint);
//从右到左
//确定贝塞尔曲线各个点的位置
path.reset();
path.moveTo(0 , mProgressY);
//从左往右绘制
Point point=null;
//w为什么是mWaveCount*3那是因为需要在不可见的区域多绘制几个曲线这样就可以形成
//波浪的效果如果只有一个mWaveCount的话那么就是一个简单的正选曲线
for (int i = 1; i <= mWaveCount * 3; i++) {
int x1 = mWaveLength * i - mWaveLength / 2 - mStep;
if (i % 2 != 0) {
path.quadTo(x1,
mProgressY - mWaterRange,
mWaveLength * i - mStep,
mProgressY);
//point=getControlPointF(new Point(x1,mProgressY - mWaterRange),new Point(mWaveLength * i - mStep,mProgressY),new Point(width / 2, mProgressY + mWaveLength * i - mWaveLength / 2));
} else {
path.quadTo(
x1,
mProgressY + mWaterRange,
mWaveLength * i - mStep,
mProgressY);
//point=getControlPointF(new Point(x1,mProgressY + mWaterRange),new Point(mWaveLength * i - mStep,mProgressY),new Point(width / 2, mProgressY + mWaveLength * i - mWaveLength / 2));
}
//Paint temp=new Paint();
//temp.setColor(Color.WHITE);
//canvas.drawCircle(width / 2,point.y-100,30,temp);
}
path.lineTo(mWaveCount * 2 * mWaveLength,mWaterHeight);
path.lineTo(0, mWaterHeight);
path.close();
canvas.drawPath(path, waterPaint);
}
/**
* 核心代码计算path
* @return
*/
private Path getPath(){
int itemWidth = mWaterWidth/2;//半个波长
Path mPath = new Path();
mPath.moveTo(-itemWidth * 3, mProgressY);//起始坐标
//核心的代码就是这里
for (int i = -3; i < 4; i++) {
int startX = i * itemWidth;
mPath.quadTo(
startX + itemWidth/2 + mStep,//控制点的X,起始点X + itemWidth/2 + offset)
getWaveHeigh( i ),//控制点的Y
startX + itemWidth + mStep,//结束点的X
mProgressY//结束点的Y
);//只需要处理完半个波长剩下的有for循环自已就添加了。
}
//下面这三句话很重要它是形成了一封闭区间让曲线以下的面积填充一种颜色大家可以把这3句话注释了看看效果。
mPath.lineTo(width,height);
mPath.lineTo(0,height);
mPath.close();
return mPath;
}
//奇数峰值是正的,偶数峰值是负数
private float getWaveHeigh(int num){
if(num % 2 == 0){
return mProgressY + mWaterRange;
}
return mProgressY - mWaterRange;
}
/**
* Author: MrGao
* CreateTime: 2018/1/4 16:29
* MethodName:
* Des水波纹动画
* Params
* Return:
**/
public void runWave(){
mStep = mStep+mWaveLength/30 ;
if(mStep>width)
mStep=0;
//mStep=rand.nextInt(20);
}
}
/**
* 绘制外层的刻度
*
* @param canvas
protected void drawOutArc(Canvas canvas) {
canvas.save();
for (int i = 0; i < 100; i++) {
if (i < mPercent) {
mOutProgressPaint.setColor(mProgressColor);
} else {
mOutProgressPaint.setColor(mOutBaseColor);
}
canvas.rotate(3.6f, width / 2, height / 2);
canvas.drawLine(width / 2, 0, height / 2, mDefaultLineLen, mOutProgressPaint);
}
canvas.restore();
}
*/
/**
* 根据 最高点,获取贝塞尔曲线的 控制点
*
* @param startPointF 开始点
* @param endPointF 结束点
* @param bezierPointF 最高点
* @return 控制点
*/
public static Point getControlPointF(Point startPointF, Point endPointF, Point bezierPointF) {
//B(t)=(1-t)^2P0+2t(1-t)P1+t^2P2;
Point controlPointF = new Point(0l, 0l);
float tmp = 0.5F;
float t = 0.5F;
controlPointF.setX((bezierPointF.x - tmp * tmp * startPointF.y - t * t * endPointF.y) / (2 * t * tmp));
controlPointF.setY((bezierPointF.y- tmp * tmp * startPointF.y - t * t * endPointF.y) / (2 * t * tmp));
return controlPointF;
}
public static class Point{
public float x,y;
public Point(float x,float y){
this.x=x;
this.y=y;
}
public void setX(float x){
this.x=x;
}
public void setY(float y){
this.y=y;
}
}
}

View File

@@ -0,0 +1,130 @@
package clock.socoolby.com.clock.widget.animatorview.animator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.Log;
import clock.socoolby.com.clock.widget.animatorview.AbstractAnimator;
//参考https://blog.csdn.net/u014112893/article/details/78556098
public class WindmillAnimator extends AbstractAnimator<WindmillAnimator.Windmill> {
@Override
public boolean run() {
for(Windmill windmill:list)
windmill.run();
return true;
}
@Override
public void onDraw(Canvas canvas) {
for(Windmill windmill:list)
windmill.onDraw(canvas);
}
@Override
public void reset() {
super.reset();
addWindmill(3);
}
private void addWindmill(int quantity){
//用于个个之间保证有足够的间距,不会都在一起
int step=width/quantity;
for(int i=0;i<quantity;i++) {
int x=step*i+rand.nextInt(step);
int y=rand.nextInt(height);
int h=rand.nextInt(height-y);
if(h<height/3) {
h = h + height/3;
}
if(y+h>height)
y=height-h-30;
int w=h*3/4;
if(x+w>width)
x=width-w-30;
if(randColor)
randomColor();
list.add(new Windmill(x,y,h,w,color));
}
}
public class Windmill{
private Paint mPaint;
private Path mPath;
private int width, height;
private int x,y;
int color;
private int lineWidth = 5;
private int windmillLeng; // 扇叶长度
private float lengthOfSide; // 扇叶小三角形的斜边长
private float angle = 0F;
private int delay=0;//使个个转动都不同
public Windmill(int x,int y,int width, int height,int color) {
this.width = width;
this.height = height;
this.x=x;
this.y=y;
this.color=color;
delay=rand.nextInt(5);
initPaint();
}
private void initPaint() {
mPaint = new Paint();
mPath = new Path();
mPaint.setColor(color);
mPaint.setStrokeWidth(lineWidth);
mPaint.setAntiAlias(true);
windmillLeng = Math.min(width, height) / 2 - 20;
lengthOfSide = (float) (windmillLeng / 10 / Math.cos(45));
}
int step=0;
public void run(){
step++;
if(step<delay)
return;
step=0;
if (angle >= 360) angle = 0;
angle += 5;
}
protected void onDraw(Canvas canvas) {
drawLines(canvas);
drawWindMills(canvas);
}
// 每个扇叶由2个三角形组成小三角形是由2个等边直角三角形组成,变成是扇叶长度的十分之一
private void drawWindMills(Canvas canvas) {
for (int i = 0; i < 3; i++) {
//Log.i("drawWindMills", height + " / " + windmillLeng);
mPath.moveTo(x+width / 2, y+ windmillLeng);
mPath.lineTo(x+width / 2 + lengthOfSide * (float) Math.sin(Math.PI / 180 * (-45 + angle + 120 * i)),
y+ windmillLeng - lengthOfSide * (float) Math.cos(Math.PI / 180 * (-45 + angle + 120 * i)));
mPath.lineTo(x+width / 2 + windmillLeng * (float) Math.sin(Math.PI / 180 * (120 * i + angle)),
y+ windmillLeng - windmillLeng * (float) Math.cos(Math.PI / 180 * (120 * i + angle)));
mPath.lineTo(x+width / 2 + lengthOfSide * (float) Math.sin(Math.PI / 180 * (45 + angle + 120 * i)),
y+ windmillLeng - lengthOfSide * (float) Math.cos(Math.PI / 180 * (45 + angle + 120 * i)));
mPath.lineTo(x+width / 2, y+ windmillLeng);
mPath.close();
canvas.drawPath(mPath, mPaint);
}
mPath.reset();
}
// 绘制风车支架
private void drawLines(Canvas canvas) {
canvas.drawLine(x+width / 3, y+height, x+width / 2, y+ windmillLeng, mPaint);
canvas.drawLine(x+width / 2, y+ windmillLeng, x+2 * width / 3, y+height, mPaint);
canvas.drawPath(mPath, mPaint);
}
}
}

View File

@@ -1,10 +0,0 @@
package clock.socoolby.com.clock.widget.animatorview.animator.bean;
public class Particle {
public int color;
public Double direction;
public float speed;
public float x = 0;
public float y = 0;
public float r=10;
}

View File

@@ -0,0 +1,8 @@
package clock.socoolby.com.clock.widget.animatorview.animator.particle;
import android.graphics.Canvas;
import android.graphics.Paint;
public interface I_PraticleDraw {
void draw(Canvas canvas, Particle particle, Paint paint);
}

View File

@@ -0,0 +1,30 @@
package clock.socoolby.com.clock.widget.animatorview.animator.particle;
public class Particle {
//运动相关
public float aX;//加速度
public float aY;//加速度Y
public float vX;//速度X
public float vY;//速度Y
public long born;//诞生时间
//绘制相关
public float x;//点位X,统一为中心点X
public float y;//点位Y统一为中心点Y
public int color;//颜色
public float r;//半径,主要用于计算所占面积,不只用于圆
public int alpha;//透明度
public I_PraticleDraw style;
public Particle clone() {
Particle clone = null;
try {
clone = (Particle) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}

View File

@@ -0,0 +1,18 @@
package clock.socoolby.com.clock.widget.animatorview.animator.particle.drawer;
import android.graphics.Canvas;
import android.graphics.Paint;
import clock.socoolby.com.clock.widget.animatorview.animator.particle.I_PraticleDraw;
import clock.socoolby.com.clock.widget.animatorview.animator.particle.Particle;
public class CircleDrawer implements I_PraticleDraw {
public static final CircleDrawer INSTANCE=new CircleDrawer();
@Override
public void draw(Canvas canvas, Particle particle, Paint paint) {
paint.setAlpha(particle.alpha);
paint.setColor(particle.color);
canvas.drawCircle(particle.x,particle.y,particle.r,paint);
}
}

View File

@@ -0,0 +1,24 @@
package clock.socoolby.com.clock.widget.animatorview.animator.particle.drawer;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import clock.socoolby.com.clock.widget.animatorview.animator.particle.I_PraticleDraw;
import clock.socoolby.com.clock.widget.animatorview.animator.particle.Particle;
public class FilletSquareDrawer implements I_PraticleDraw {
public static FilletSquareDrawer getInstance() {
return new FilletSquareDrawer();
}
@Override
public void draw(Canvas canvas, Particle particle, Paint paint) {
paint.setAlpha(particle.alpha);
paint.setColor(particle.color);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawRoundRect(particle.x - particle.r, particle.y - particle.r, particle.x + particle.r, particle.y + particle.r, particle.r / 2, particle.r / 2, paint);
}else
canvas.drawRect(particle.x-particle.r,particle.y-particle.r,particle.x+particle.r,particle.y+particle.r,paint);
}
}

View File

@@ -0,0 +1,4 @@
package clock.socoolby.com.clock.widget.animatorview.animator.particle.drawer;
public class PolygonDrawer {
}

View File

@@ -0,0 +1,20 @@
package clock.socoolby.com.clock.widget.animatorview.animator.particle.drawer;
import android.graphics.Canvas;
import android.graphics.Paint;
import clock.socoolby.com.clock.widget.animatorview.animator.particle.I_PraticleDraw;
import clock.socoolby.com.clock.widget.animatorview.animator.particle.Particle;
public class SquareDrawer implements I_PraticleDraw {
public static SquareDrawer getInstance(){
return new SquareDrawer();
}
@Override
public void draw(Canvas canvas, Particle particle, Paint paint) {
paint.setAlpha(particle.alpha);
paint.setColor(particle.color);
canvas.drawRect(particle.x-particle.r,particle.y-particle.r,particle.x+particle.r,particle.y+particle.r,paint);
}
}

View File

@@ -0,0 +1,45 @@
package clock.socoolby.com.clock.widget.animatorview.animator.particle.drawer;
import android.graphics.Canvas;
import android.graphics.Paint;
import clock.socoolby.com.clock.widget.animatorview.animator.particle.I_PraticleDraw;
import clock.socoolby.com.clock.widget.animatorview.animator.particle.Particle;
public class TriangleDraw implements I_PraticleDraw {
public static TriangleDraw getInstance(){
return new TriangleDraw();
}
Direction direction;
public TriangleDraw() {
this(Direction.UP);
}
public TriangleDraw(Direction direction) {
this.direction = direction;
}
@Override
public void draw(Canvas canvas, Particle particle, Paint paint) {
canvas.save();
paint.setAlpha(particle.alpha);
paint.setColor(particle.color);
canvas.rotate(direction.degree,particle.x,particle.y);
canvas.drawLine(particle.x, particle.y-particle.r, particle.x-particle.r,particle.y+particle.r, paint);
canvas.drawLine(particle.x, particle.y-particle.r, particle.x+particle.r,particle.y+particle.r, paint);
canvas.drawLine(particle.x-particle.r, particle.y+particle.r, particle.x+particle.r,particle.y+particle.r, paint);
canvas.restore();
}
public enum Direction{
LEFT(-270),RIGHT(-90),UP(0),DOWN(-180);
public float degree;
Direction(float degree) {
this.degree = degree;
}
}
}

View File

@@ -56,12 +56,12 @@ public class AutoScrollTextView extends android.support.v7.widget.AppCompatTextV
textLength = paint.measureText(text);
viewWidth = getWidth();
Log.e("zmm","--->"+textLength+"--->"+viewWidth+"-->"+getTextSize());
Log.e(TAG,"--->"+textLength+"--->"+viewWidth+"-->"+getTextSize());
if(viewWidth == 0)
{
viewWidth = 600;//获取屏幕的宽度
}
Log.e("zmm","屏幕的宽度-->"+ 600);
Log.e(TAG,"屏幕的宽度-->"+ 600);
step = textLength;//文字真实的长度
temp_view_plus_text_length = viewWidth/2 + textLength;
temp_view_plus_two_text_length = viewWidth + textLength * 2;//文字移动的距离应该是控件的长度+左边一个文字的长度+右边一个文字的长度

View File

@@ -0,0 +1,180 @@
package clock.socoolby.com.clock.widget.textview;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.text.TextPaint;
import android.text.style.ReplacementSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@SuppressLint("AppCompatCustomView")
public class DigitTextView extends TextView {
public DigitTextView(Context context) {
super(context);
}
public DigitTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public DigitTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public DigitTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
/**
* 使TextView中不同大小字体垂直居中
*/
public static class DigitSpan extends ReplacementSpan {
private int fontSizeSp; //字体大小sp
public DigitSpan(){
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
String text1 = text.subSequence(start, end).toString();
//Log.d("DigitSpan","getSize text:"+text+"\tstart:"+start+"\tend:"+end+"\tsubStr:"+text1);
//Paint p = getCustomTextPaint(paint);
return (int) getCharWidth("8",paint)*text1.length();
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
String text1 = text.subSequence(start, end).toString();
//Log.d("DigitSpan","draw text:"+text+"\tstart:"+start+"\tend:"+end+"\tsubStr:"+text1);
Paint p = getCustomTextPaint(paint);
Paint.FontMetricsInt fm = p.getFontMetricsInt();
int baseCharWitch= getCharWidth("8",paint);
int currentCharWidth,moveWidthX;
for(int i=0;i<text1.length();i++) {
String currentChar=String.valueOf(text1.charAt(i));
currentCharWidth= getCharWidth(currentChar,paint);
moveWidthX=baseCharWitch*i+(baseCharWitch-currentCharWidth)/2;
canvas.drawText(currentChar, x+moveWidthX, y - ((y + fm.descent + y + fm.ascent) / 2 - (bottom + top) / 2), p); //此处重新计算y坐标使字体居中
}
}
private TextPaint getCustomTextPaint(Paint srcPaint) {
TextPaint paint = new TextPaint(srcPaint);
//paint.setTextSize(ViewUtils.getSpPixel(mContext, fontSizeSp)); //设定字体大小, sp转换为px
return paint;
}
}
public static class DigitFlagSpan extends ReplacementSpan {
public DigitFlagSpan(){
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
String text1 = text.subSequence(start, end).toString();
return (int) getCharWidth(":",paint);
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
String text1 = text.subSequence(start, end).toString();
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
canvas.drawText(text1, x, y - ((y + fm.descent + y + fm.ascent) / 2 - (bottom + top) / 2), paint); //此处重新计算y坐标使字体居中
}
}
public static class DigitSmallSpan extends ReplacementSpan {
private float fontSizeSp; //字体大小sp
public DigitSmallSpan(){
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
String text1 = text.subSequence(start, end).toString();
//Log.d("DigitSpan","getSize text:"+text+"\tstart:"+start+"\tend:"+end+"\tsubStr:"+text1);
fontSizeSp=paint.getTextSize()/2;
Paint p = getCustomTextPaint(paint);
return (int) getCharWidth("8",p)*text1.length();
}
int currentCharWidth,moveWidthX;
String currentChar;
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
String text1 = text.subSequence(start, end).toString();
if(text1.length()<=1)
return;
//Log.d("DigitSpan","draw text:"+text+"\tstart:"+start+"\tend:"+end+"\tsubStr:"+text1);
Paint.FontMetricsInt fmBefor=paint.getFontMetricsInt();
Paint p = getCustomTextPaint(paint);
Paint.FontMetricsInt fm = p.getFontMetricsInt();
int baseCharWitch= getCharWidth("8",p);
for(int i=1;i<text1.length();i++) {
currentChar=String.valueOf(text1.charAt(i));
currentCharWidth= getCharWidth(currentChar,paint);
moveWidthX=baseCharWitch*i+(baseCharWitch-currentCharWidth)/2;
//canvas.drawText(currentChar, x+moveWidthX, bottom+fm.ascent, p); //此处重新计算y坐标使字体居中
canvas.drawText(currentChar, x+moveWidthX, bottom-fmBefor.descent, p); //此处重新计算y坐标使字体居中
}
}
private TextPaint getCustomTextPaint(Paint srcPaint) {
TextPaint paint = new TextPaint(srcPaint);
paint.setTextSize(fontSizeSp); //设定字体大小, sp转换为px
return paint;
}
}
public static class SuperSubSpan extends ReplacementSpan
{
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm)
{
text = text.subSequence(start, end);
String[] parts = text.toString().split(",");
Paint p = getSuperSubPaint(paint);
return (int) Math.max(p.measureText(parts[0]), p.measureText(parts[1]));
}
private static TextPaint getSuperSubPaint(Paint src)
{
TextPaint paint = new TextPaint(src);
paint.setTextSize(src.getTextSize() / 2.5f);
return paint;
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint)
{
text = text.subSequence(start, end);
String[] parts = text.toString().split(",");
Paint p = getSuperSubPaint(paint);
float width1 = p.measureText(parts[0]);
float width2 = p.measureText(parts[1]);
float maxWidth = Math.max(width1, width2);
canvas.drawText(parts[0], x + (maxWidth - width1), y - (bottom - top) / 3f, p);
canvas.drawText(parts[1], x + (maxWidth - width2), y + (bottom - top) / 10f, p);
}
}
private static int getCharWidth(String str, Paint paint){
return (int)paint.measureText(str);
}
}