Android Timer使ListView有时会忽略点击次数

时间:2017-07-12 04:23:33

标签: java android multithreading listview android-asynctask

我正在创建一个简单的小游戏来练习我的编码,但我偶然发现了一个问题。这不是游戏破坏,但它确实很烦人。在我的游戏中,我有一个类Tappable,这是我的ListView填充。每次点击ListView元素时,我的OnItemClickListener只会增加一个Tappable的数量。

长话短说,我使用自定义ArrayAdapter并且我在一个带有处理程序的循环上运行AsyncTask,以便计算你赚了多少钱。这个定时AsyncTask的实现已经完成,所以我可以将计算放在另一个线程上,然后更新UI onPostExecute。但是,当我点击ListView元素时,会完全忽略一些点击。更严重的是,定时AsyncTasks之间的间隔越小。

这是我的MainActivity.java

import android.os.AsyncTask;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Locale;

public class MainActivity extends AppCompatActivity {
    TextView moneyView;
    ListView tappablesView;
    ArrayList<Tappable> tappables = new ArrayList<>();
    TappableAdapter tappableAdapter;
    double money = 0; // Total amount of money you have
    double time; // Var to do checks on the amount of time to finish AsyncTask

    // Here is the stuff that I use as my Timer.
    int millisecondsToDelay = 30;
    Handler handler = new Handler();

    public class WorldUpdater extends AsyncTask<Void, Integer, Double> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();

            //This is for checking how long it takes my Tasks to complete
            time = System.currentTimeMillis();
        }

        @Override
        protected Double doInBackground(Void... params) {
            // Var to hold amount to add to money
            double moneyAddable = 0;
            // Go through the tappables arraylist
            for (int i = 0; i < tappables.size(); i++) {
                Tappable t = tappables.get(i);
                // increase the progress to a payout for a tappable
                if (t.getAmount() > 0) {
                    t.updateProgress();
                }
                // add any money that can be collected to moneyAddable
                moneyAddable += t.collectMoney();
            }

            // To be processed in onPostExecute
            return moneyAddable;
        }

        @Override
        protected void onPostExecute(Double m) {
            super.onPostExecute(m);

            // add the moneyAddable from doInBackground to the amount of money you have
            money += m;

            // Set the moneyView text to show how much money you have
            updateMoneyView();
            // Tappable progress increased, so show that in the list view
            tappableAdapter.notifyDataSetChanged();

            // Now we see how long the task took
            time = System.currentTimeMillis() - time;

            // If the task took longer than the amount I want it to delay, then say how long it took
            if (time > millisecondsToDelay) {
                Log.i("Timer info", "Too long: " + time);
            }

            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    new WorldUpdater().execute();
                }
            }, (((long) (millisecondsToDelay - time)) > 0)? (long) (millisecondsToDelay - time): (long) 0);
        }
    }

    public void updateMoneyView() {
        moneyView.setText(String.format(Locale.getDefault(), "$%,.2f", money));
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        moneyView = (TextView) findViewById(R.id.Money);
        tappablesView = (ListView) findViewById(R.id.TappablesView);

        // The two tappables in the listview
        tappables.add(new Tappable("Buy me!", .25, 1, 1500));
        tappables.add(new Tappable("Buy me as well!", 1, 2.25, 1000));

        tappableAdapter = new TappableAdapter(this, tappables);
        tappablesView.setAdapter(tappableAdapter);

        tappablesView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // On item click, increase the amount of the tappable that was clicked by 1
                tappables.get(position).buyAmount(1);
            }
        });

        updateMoneyView();

        // Start my timer
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                new WorldUpdater().execute();
            }
        }, millisecondsToDelay);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        handler.removeCallbacksAndMessages(null);
    }
}

这是我的Tappable.java

public class Tappable {

    private String title;
    private double revenue, cost, revenuePerItem, costPerItem;
    private int progress, progressNeeded, progressStep, amount;
    private boolean canCollect = false;
    private double amountToCollect = 0;

    public Tappable(String title, double r, double c, int ps) {
        this.title = title;
        revenuePerItem = r;
        amount = 0;
        revenue = revenuePerItem * amount;
        costPerItem = c;
        cost = costPerItem * (amount + 1);
        progressNeeded = 100000;
        progress = 0;
        progressStep = ps;
    }

    public void buyAmount(int n) {
        amount += n;
        updateCost();
        updateRevenue();
    }

    public void updateProgress() {
        progress += progressStep;
        if (progress >= progressNeeded) {
            progress = 0;
            amountToCollect += getRevenue();
            canCollect = true;
        }
    }

    public double collectMoney() {
        double amountCollectable = 0;
        if (canCollect) {
            amountCollectable = amountToCollect;
            amountToCollect = 0;
            canCollect = false;
        }
        return amountCollectable;
    }

    public void updateCost() {
        cost = costPerItem * (amount + 1);
    }

    public void updateRevenue() {
        revenue = revenuePerItem * amount;
    }

    public String getTitle() {
        return title;
    }

    public int getAmount() {
        return amount;
    }

    public double getCost() {
        return cost;
    }

    public int getProgress() {
        return progress;
    }

    public int getProgressNeeded() {
        return progressNeeded;
    }

    public double getRevenue() {
        return revenue;
    }
}

这是我的TappableAdapter.java

import android.app.Activity;
import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Locale;

public class TappableAdapter extends BaseAdapter {

    private final Activity context;
    private final ArrayList<Tappable> tappables;
    private final Resources res;
    private final LayoutInflater inflater;

    public TappableAdapter(Activity context, ArrayList<Tappable> t) {
        this.context = context;
        tappables = t;
        res = context.getResources();
        inflater = context.getLayoutInflater();
    }

    @Override
    public int getCount() {
        return tappables.size();
    }

    @Override
    public Object getItem(int position) {
        return tappables.get(position);
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        Tappable t = tappables.get(position);

        View rowView = inflater.inflate(R.layout.list_item, null, true);

        TextView titleView, amountView, costView, revenueView;
        titleView = (TextView) rowView.findViewById(R.id.Title);
        amountView = (TextView) rowView.findViewById(R.id.Amount);
        costView = (TextView) rowView.findViewById(R.id.Cost);
        revenueView = (TextView) rowView.findViewById(R.id.Revenue);

        ProgressBar p = (ProgressBar) rowView.findViewById(R.id.progressBar);

        titleView.setText(t.getTitle());
        amountView.setText("Amount: " + t.getAmount());
        costView.setText(String.format(Locale.getDefault(), "$%,.2f", t.getCost()));
        revenueView.setText(String.format(Locale.getDefault(), "$%,.2f", t.getRevenue()));

        p.setMax(t.getProgressNeeded());
        p.setProgress(t.getProgress());

        return rowView;
    }
}

这是我的activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.jake.mypointclicker.MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:text="Simple Point Clicker"
        android:textColor="@android:color/background_light"
        android:textSize="36sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintVertical_weight="0" />

    <TextView
        android:id="@+id/Money"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:text="$123,456,789.00"
        android:textColor="@android:color/holo_orange_dark"
        android:textSize="30sp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        android:layout_marginEnd="8dp"
        app:layout_constraintVertical_weight="0" />

    <Button
        android:id="@+id/button"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:text="Button"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/Money"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintVertical_weight="0" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button"
        app:layout_constraintVertical_weight="0">

        <ListView
            android:id="@+id/TappablesView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

</android.support.constraint.ConstraintLayout>

这是我的list_item.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="wrap_content"
    android:background="@android:color/black"
    android:orientation="vertical"
    android:padding="8dp">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/Title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="top|left"
            android:text="Test Title"
            android:textColor="@android:color/holo_blue_light"
            android:textSize="20sp" />

        <TextView
            android:id="@+id/Revenue"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="top|right"
            android:text="$123,456,789.00"
            android:textColor="@android:color/holo_green_light"
            android:textSize="20sp" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/Amount"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="top|left"
            android:text="Amount: 123456789" />

        <TextView
            android:id="@+id/Cost"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="top|right"
            android:text="$123,456,789.00"
            android:textColor="@android:color/holo_red_light"
            android:textSize="20sp" />
    </LinearLayout>

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

我尝试了很多方法来做我的计时器。我尝试使用具有相同效果的timertask的Timer。第25行左右是我的变量延迟多长时间。降低会增加忽略的点击量。提升它会产生相反的效果,但会使进度条看起来不那么平滑。就像我说的那样,它不是游戏制动问题,但是我不明白为什么当我尝试经常更新UI时,为什么不会调用onItemClick。监听器本身是否在UI线程上,当我在AsyncTask中更新UI时它是否被阻止?

0 个答案:

没有答案