android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
dataBinding {
enabled = true
}
}
dependencies {
...
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.2.0'
implementation 'com.squareup.retrofit2:retrofit:2.2.0'
implementation 'com.jakewharton:butterknife:9.0.0'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'org.greenrobot:eventbus:3.0.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0'
...
}
dependencies {
...
classpath 'com.jakewharton:butterknife-gradle-plugin:9.0.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
public interface WeatherService {
@GET("{city_code}")
Observable<WeatherBean> getCall(@Path("city_code") String code);
}
@SuppressLint("CheckResult")
private void requestWeather(String cityId) {
bitmaps.clear();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://t.weather.sojson.com/api/weather/city/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
final WeatherService weatherService = retrofit.create(WeatherService.class);
weatherService.getCall(cityId)
.subscribeOn(Schedulers.io())
.flatMap((Function<WeatherBean, Observable<WeatherBean>>) weatherBean -> {
...
for (WeatherBean.DataBean.ForecastBean forecastBean : weatherBean.getData().getForecast()) {
...
String url = "https://cdn.heweather.com/cond_icon/" + preferences.getString(forecastBean.getType(), "未知");
FutureTarget<Bitmap> target = Glide.with(getApplicationContext())
.asBitmap()
.load(url)
.submit();
final Bitmap bitmap = target.get();
dataArrayList.add(new MyCurveView.WeatherData(low, high, date, type, bitmap));
}
...
return Observable.fromArray(weatherBean);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<WeatherBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@SuppressLint("SetTextI18n")
@Override
public void onNext(WeatherBean weatherBean) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
static class WeatherData {
private float lowTemp;
private float highTemp;
private int date;
private String type;
private Bitmap typeBitmap;
WeatherData(float lowTemp, float highTemp, int date, String type, Bitmap typeBitmap) {
this.lowTemp = lowTemp;
this.highTemp = highTemp;
this.date = date;
this.type = type;
this.typeBitmap = typeBitmap;
}
...
}
public void setProgress(int averageHigh, int averageLow, final int low, int top, ArrayList<WeatherData> innerData) {
arrayList(innerData, top, low, averageHigh, averageLow);
ValueAnimator animatorHigh = ValueAnimator.ofInt(0, top);
animatorHigh.setDuration(1000);
animatorHigh.setInterpolator(new AccelerateInterpolator());
animatorHigh.addUpdateListener(valueAnimator -> {
mHighPercent = (int)valueAnimator.getAnimatedValue();
invalidate();
});
ValueAnimator animatorLow = ValueAnimator.ofInt(0, low);
animatorLow.setDuration(1000);
animatorLow.setInterpolator(new AccelerateInterpolator());
animatorLow.addUpdateListener(valueAnimator -> {
mLowPercent = (int)valueAnimator.getAnimatedValue();
});
AnimatorSet set = new AnimatorSet();
set.playTogether(animatorHigh, animatorLow);
set.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
isAnimation = true;
}
@Override
public void onAnimationEnd(Animator animation) {
isAnimation = false;
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
set.start();
}
@SuppressWarnings("PointlessArithmeticExpression")
private void arrayList(ArrayList<WeatherData> innerData, int max, int min, int averageHigh, int averageLow) {
high = averageHigh;
low = averageLow;
dataArray.clear();
dataArray.addAll(innerData);
for (int i = 0; i < innerData.size(); i++) {
dataArray.get(i).setHighTemp((innerData.get(i).getHighTemp() - averageHigh) / (max - 0));
dataArray.get(i).setLowTemp((averageLow - innerData.get(i).getLowTemp()) / (min - 0));
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
measureWidth = widthSize;
measureHeight = heightSize;
mTempWidth = measureWidth / 6;
setMeasuredDimension(widthSize, heightSize);
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (dataArray.size() <= 0) {
drawNoDataText(canvas);
} else {
float startX = mStartX + (mTempWidth / 2);
for (int i = 0; i < dataArray.size(); i++) {
RectF rectF = new RectF(startX - 30, 300, startX + 30, 360);
canvas.drawBitmap(dataArray.get(i).getTypeBitmap(), null, rectF, mCurvePaint);
float highTextWidth = mTempTextPaint.measureText((int)(high + mHighPercent * dataArray.get(i).getHighTemp()) + "");
float highTextStartX = startX - highTextWidth / 2;
drawTempText(canvas, (int)(high + getHighTempByPercent(i)) + "", highTextStartX, (140 - curve_ratio * getHighTempByPercent(i)));
canvas.drawCircle(startX, (160 - curve_ratio * getHighTempByPercent(i)), 5, circlePaint);
if (i == 0) {
highPath.moveTo(startX, (160 - curve_ratio * getHighTempByPercent(i)));
highControlPt1X = startX + mTempWidth / 4;
highControlPt1Y = 160 - curve_ratio * getHighTempByPercent(i);
highControlPt2X = startX + (mTempWidth / 4) * 3;
highControlPt2Y = ((160 - curve_ratio * getHighTempByPercent(i + 1))) - (((160 - curve_ratio * getHighTempByPercent(i + 2))) - ((160 - curve_ratio * getHighTempByPercent(i)))) / 4;
highPath.cubicTo(
highControlPt1X, highControlPt1Y,
highControlPt2X, highControlPt2Y,
startX + mTempWidth, (160 - curve_ratio * getHighTempByPercent(i + 1)));
canvas.drawPath(highPath, mCurvePaint);
highPath.reset();
}
if (i != 0 && i < dataArray.size() - 2) {
highPath.moveTo(startX, (160 - curve_ratio * getHighTempByPercent(i)));
highControlPt1X = startX + mTempWidth / 4;
highControlPt1Y = ((160 - curve_ratio * getHighTempByPercent(i))) + (((160 - curve_ratio * getHighTempByPercent(i + 1))) - ((160 - curve_ratio * getHighTempByPercent(i - 1)))) / 4;
highControlPt2X = startX + (mTempWidth / 4) * 3;
highControlPt2Y = ((160 - curve_ratio * getHighTempByPercent(i + 1))) - (((160 - curve_ratio * getHighTempByPercent(i + 2))) - ((160 - curve_ratio * getHighTempByPercent(i)))) / 4;
highPath.cubicTo(
highControlPt1X, highControlPt1Y,
highControlPt2X, highControlPt2Y,
startX + mTempWidth, (160 - curve_ratio * getHighTempByPercent(i + 1)));
canvas.drawPath(highPath, mCurvePaint);
highPath.reset();
}
if (i == dataArray.size() - 2) {
highPath.moveTo(startX, (160 - curve_ratio * getHighTempByPercent(i)));
highControlPt1X = startX + mTempWidth / 4;
highControlPt1Y = ((160 - curve_ratio * getHighTempByPercent(i))) + (((160 - curve_ratio * getHighTempByPercent(i + 1))) - ((160 - curve_ratio * getHighTempByPercent(i - 1)))) / 4;
highControlPt2X = startX + (mTempWidth / 4) * 3;
highControlPt2Y = 160 - curve_ratio * getHighTempByPercent(i + 1);
highPath.cubicTo(
highControlPt1X, highControlPt1Y,
highControlPt2X, highControlPt2Y,
startX + mTempWidth, (160 - curve_ratio * getHighTempByPercent(i + 1)));
canvas.drawPath(highPath, mCurvePaint);
highPath.reset();
}
...
float dayTextWidth = mTextPaint.measureText(dataArray.get(i).getDate() + "日");
float dayStartX = startX - dayTextWidth / 2;
float dayTextStartY = 40 + getFontAscentHeight(mTextPaint);
drawDayText(canvas, dataArray.get(i).getDate() + "日", dayStartX, dayTextStartY);
float typeTextWidth = mTextPaint.measureText(dataArray.get(i).getType());
float typeTextStartX = startX - typeTextWidth / 2;
float typeTextStartY = measureHeight - 40 - getFontDescentHeight(mTextPaint);
canvas.drawText(dataArray.get(i).getType(), typeTextStartX, typeTextStartY, mTextPaint);
startX = startX + mTempWidth;
}
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int dispatchCurrX = (int) ev.getX();
int dispatchCurrY = (int) ev.getY();
switch (ev.getAction()) {
...
case MotionEvent.ACTION_MOVE:
float deltaX = dispatchCurrX - dispatchTouchX;
float deltaY = dispatchCurrY - dispatchTouchY;
if (Math.abs(deltaY) - Math.abs(deltaX) > 0) {
getParent().requestDisallowInterceptTouchEvent(false);
}
if ((dispatchCurrX - dispatchTouchX) > 0 && mStartX == 0) {
getParent().requestDisallowInterceptTouchEvent(false);
} else if ((dispatchCurrX - dispatchTouchX) < 0 && mStartX == -getMoveLength()) {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
...
}
dispatchTouchX = dispatchCurrX;
dispatchTouchY = dispatchCurrY;
return super.dispatchTouchEvent(ev);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isAnimation) {
return true;
}
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = event.getX();
if (isFling) {
removeCallbacks(mScrollRunnable);
isFling = false;
}
break;
case MotionEvent.ACTION_MOVE:
float currX = event.getX();
mStartX += currX - lastX;
if ((currX - lastX) > 0) {
if (mStartX > 0) {
mStartX = 0;
}
} else {
if (-mStartX > getMoveLength()) {
mStartX = -getMoveLength();
}
}
lastX = currX;
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.computeCurrentVelocity(1000);
if (Math.abs(mVelocityTracker.getXVelocity()) > 100
&& !isFling && measureWidth < dataArray.size() * mTempWidth) {
mScrollRunnable = new ScrollRunnable(mVelocityTracker.getXVelocity() / 5);
this.post(mScrollRunnable);
}
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
public class CityActivity extends AppCompatActivity {
...
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityCityBinding activityCityBinding = DataBindingUtil.setContentView(this, R.layout.activity_city);
activityCityBinding.setCityViewHolder(new CityViewHolder());
...
}
...
public class CityViewHolder {
public void afterTextChanged(Editable s) {
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="cityPresenter"
type="com.sk.simpleweather.CityActivity.CityViewHolder" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="20dp"
android:layout_marginStart="30dp"
android:layout_marginEnd="30dp"
android:orientation="vertical">
<EditText
android:id="@+id/search_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:background="@drawable/et_bgd"
android:focusable="true"
android:textSize="16sp"
android:maxLines="1"
android:singleLine="true"
android:hint="輸入城市"
android:textColorHint="#40000000"
android:afterTextChanged="@{cityPresenter.afterTextChanged}"/>
...
</LinearLayout>
</layout>
public interface CityService {
@GET("find")
Observable<CityBean> getCall(@Query("location") String location,
@Query("key") String key);
}
public void afterTextChanged(Editable s) {
...
if (s.toString().equals("")) {
return;
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://search.heweather.net/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
CityService cityService = retrofit.create(CityService.class);
cityService.getCall(s.toString(), KEY)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<CityBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(CityBean cityBean) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
...
}
});
}
public class CitysAdapter extends RecyclerView.Adapter<CitysAdapter.ViewHolder>{
private ArrayList<String> citys;
private Context mContext;
private OnItemClick mOnItemClick;
CitysAdapter(ArrayList<String> citys, Context parentContext) {
this.citys = citys;
mContext = parentContext;
}
void setOnItemClick(OnItemClick onItemClick) {
mOnItemClick = onItemClick;
}
interface OnItemClick {
void onClick(String city, int position);
}
static class ViewHolder extends RecyclerView.ViewHolder {
private LinearLayout layout;
private TextView textView;
ViewHolder(@NonNull View itemView) {
super(itemView);
layout = itemView.findViewById(R.id.city_layout);
textView = itemView.findViewById(R.id.city);
}
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
if (mContext == null) {
mContext = viewGroup.getContext();
}
View view = LayoutInflater.from(mContext).inflate(R.layout.city_item, viewGroup, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
viewHolder.textView.setText(citys.get(position));
viewHolder.layout.setOnClickListener(v -> mOnItemClick.onClick(citys.get(position), position));
}
@Override
public int getItemCount() {
return citys.size();
}
}
public class CityMessageEvent {
private String name;
private String cityId;
...
}
private CityMessageEvent messageEvent;
@Override
public void onClick(String city, int position) {
if (mCityBean != null) {
messageEvent.setName(city);
messageEvent.setCityId(mCityBean.getHeWeather6().get(0).getBasic().get(position).getCid().replace("CN", ""));
EventBus.getDefault().postSticky(messageEvent);
}
finish();
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onMessageEvent(CityMessageEvent messageEvent) {
requestWeather(messageEvent.getCityId());
}
項目地址:GitHub
https://github.com/songkai0825/SimpleWeather
版權聲明:本文為CSDN博主「Kai_0825」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結及本聲明。https://blog.csdn.net/songkai0825/article/details/95257837推薦↓↓↓
涵蓋:程式設計師大咖、源碼共讀、程式設計師共讀、數據結構與算法、黑客技術和網絡安全、大數據科技、編程前端、Java、Python、Web編程開發、Android、iOS開發、Linux、資料庫研發、幽默程式設計師等。