Android开发:如何实现基于日期与用户输入自动更新的TextView?
Hey there! As someone who’s navigated the Android Studio learning curve, I’ll walk you through building this feature step by step. Let’s split it into actionable, easy-to-follow parts:
First, create a layout with inputs for the target value and monthly date, plus a TextView to display progress. Here’s a sample activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp" android:gap="16dp"> <!-- Target Value Input --> <EditText android:id="@+id/et_target_value" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter target value (e.g., 100000)" android:inputType="numberDecimal|numberSigned"/> <!-- Monthly Date Input --> <EditText android:id="@+id/et_monthly_date" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter monthly date (e.g., 15)" android:inputType="number"/> <!-- Confirm Button --> <Button android:id="@+id/btn_set_goal" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Set Goal"/> <!-- Progress Display --> <TextView android:id="@+id/tv_progress" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Current Progress: 0" android:textSize="20sp" android:textStyle="bold"/> </LinearLayout>
We’ll use Java’s modern java.time API (available on API 26+, or use desugar support for older versions) for clean date handling. Here’s a helper method to get remaining days:
import java.time.LocalDate; import java.time.temporal.ChronoUnit; public long getRemainingDays(int targetDay) { LocalDate today = LocalDate.now(); LocalDate nextTargetDate; // If today is past the target day, use next month's date if (today.getDayOfMonth() > targetDay) { nextTargetDate = today.plusMonths(1).withDayOfMonth(targetDay); } else { nextTargetDate = today.withDayOfMonth(targetDay); } // Handle edge cases (e.g., 31st in February) if (nextTargetDate.getDayOfMonth() != targetDay) { nextTargetDate = nextTargetDate.withDayOfMonth(nextTargetDate.lengthOfMonth()); } return ChronoUnit.DAYS.between(today, nextTargetDate); }
Step 3.1: Compute Daily Amount
Once you have remaining days, calculate the daily increment:
double targetValue = Double.parseDouble(et_target_value.getText().toString()); long remainingDays = getRemainingDays(targetDay); // If today is the target date, jump straight to the target value double dailyIncrement = remainingDays == 0 ? targetValue : targetValue / remainingDays;
Step 3.2: Use WorkManager for Daily Updates
WorkManager is the recommended tool for repeating background tasks (even if the app is closed). First, add the dependency to your module-level build.gradle:
dependencies { implementation "androidx.work:work-runtime:2.8.1" }
Create a worker class to handle progress updates:
import android.content.Context; import android.content.Intent; import android.preference.PreferenceManager; import androidx.annotation.NonNull; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.work.Worker; import androidx.work.WorkerParameters; public class ProgressUpdateWorker extends Worker { public ProgressUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @NonNull @Override public Result doWork() { Context context = getApplicationContext(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); // Load saved values double currentProgress = prefs.getFloat("current_progress", 0f); double dailyIncrement = prefs.getFloat("daily_increment", 0f); double targetValue = prefs.getFloat("target_value", 0f); // Update progress (don't exceed target) currentProgress += dailyIncrement; if (currentProgress > targetValue) currentProgress = targetValue; // Save updated progress prefs.edit() .putFloat("current_progress", (float) currentProgress) .apply(); // Notify the activity to update the TextView Intent intent = new Intent("PROGRESS_UPDATED"); intent.putExtra("current_progress", currentProgress); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); return Result.success(); } }
Step 3.3: Schedule the Daily Task
In your MainActivity, schedule the worker when the user clicks "Set Goal":
import androidx.work.PeriodicWorkRequest; import androidx.work.WorkManager; import java.util.concurrent.TimeUnit; // Inside btn_set_goal's onClickListener int targetDay = Integer.parseInt(et_monthly_date.getText().toString()); double targetValue = Double.parseDouble(et_target_value.getText().toString()); long remainingDays = getRemainingDays(targetDay); double dailyIncrement = remainingDays == 0 ? targetValue : targetValue / remainingDays; // Save values to persist across app restarts SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); prefs.edit() .putFloat("target_value", (float) targetValue) .putInt("target_day", targetDay) .putFloat("daily_increment", (float) dailyIncrement) .putFloat("current_progress", 0f) // Reset progress for new goal .apply(); // Cancel any existing work first WorkManager.getInstance(this).cancelUniqueWork("daily_progress_update"); // Schedule daily updates PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder( ProgressUpdateWorker.class, 1, TimeUnit.DAYS) .setInitialDelay(0, TimeUnit.MILLISECONDS) // Start immediately .build(); WorkManager.getInstance(this).enqueueUniqueWork( "daily_progress_update", ExistingPeriodicWorkPolicy.REPLACE, workRequest); // Update TextView right away updateProgressTextView(0);
Step 3.4: Receive Updates in the Activity
Add a broadcast receiver to update the TextView when progress changes:
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import androidx.localbroadcastmanager.content.LocalBroadcastManager; private BroadcastReceiver progressUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { double currentProgress = intent.getDoubleExtra("current_progress", 0); updateProgressTextView(currentProgress); } }; @Override protected void onResume() { super.onResume(); LocalBroadcastManager.getInstance(this).registerReceiver( progressUpdateReceiver, new IntentFilter("PROGRESS_UPDATED")); // Load saved progress when returning to the app SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); double currentProgress = prefs.getFloat("current_progress", 0f); updateProgressTextView(currentProgress); } @Override protected void onPause() { super.onPause(); LocalBroadcastManager.getInstance(this).unregisterReceiver(progressUpdateReceiver); } private void updateProgressTextView(double progress) { tv_progress.setText(String.format("Current Progress: %.2f", progress)); }
Don’t skip checking for valid inputs to avoid crashes:
// Inside btn_set_goal's onClickListener String targetValueStr = et_target_value.getText().toString(); String targetDayStr = et_monthly_date.getText().toString(); if (targetValueStr.isEmpty() || targetDayStr.isEmpty()) { Toast.makeText(this, "Please fill all fields", Toast.LENGTH_SHORT).show(); return; } int targetDay = Integer.parseInt(targetDayStr); if (targetDay < 1 || targetDay > 31) { Toast.makeText(this, "Enter a valid date (1-31)", Toast.LENGTH_SHORT).show(); return; } double targetValue = Double.parseDouble(targetValueStr); if (targetValue <= 0) { Toast.makeText(this, "Target value must be positive", Toast.LENGTH_SHORT).show(); return; }
内容的提问来源于stack exchange,提问作者Lars Bing

