質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.19%

AndroidStudioでのカレンダー作成で取得ずれが起きる

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,366

Aies

score 21

以下のようなコードでカレンダーをGridViewにて作成しているのですが、今月のクリックイベントのときはちゃんとArrayListに値が入っていて正常に日付を取り出せるのですが、Prevボタンを押して前の月に移動したときや次の月の、クリックイベントではArrayListがずれており(前の月の日付列が入っていなければいけないのに、一番最初に保持した値が残っている)、カレンダーの表記上はちゃんと出ているのですが、正常にクリックした日付を取得できません。調べても解決できなそうなのでよろしければご助言お願いいたします。

Calendar Class

private List<Date> dateArray = new ArrayList();
    private Button prevButton, nextButton;
    private CalendarAdapter mCalendarAdapter;
    private GridView calendarGridView;
    private DateManager mDateManager;
    private KakeiboDatabase kDB;
    int foodEx,dailyNe,eduEx,lesson,entEx,other;
    int total;
    float foodExPar, dailyNePar, eduExPar, lessonPar, entExPar, otherPar;
    float oneHan = 100;


    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        mDateManager = new DateManager();
        dateArray = mDateManager.getDays();
        String date = titleText.getText().toString().substring(0,7);
        date = date + "-" + dateArray.get(position).toString().substring(8,10); //ここでポジションがあっているのにdateArrayがずれていて前月や次の月の日付取得ができません
        String judge = readData(date);
        if(judge!=null) {
            Intent intent = new Intent(getApplication(), DailyData.class);
            intent.putExtra("CalendarID", date);
            startActivity(intent);
        }
    }


    private static Calendar instance = null;

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



        instance = this;

        titleText = findViewById(R.id.titleText);
        prevButton = findViewById(R.id.prevButton);
        prevButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCalendarAdapter.prevMonth();
                titleText.setText(mCalendarAdapter.getTitle());
                listenerSet();
            }
        });
        nextButton = findViewById(R.id.nextButton);
        nextButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCalendarAdapter.nextMonth();
                titleText.setText(mCalendarAdapter.getTitle());
                listenerSet();
            }
        });
        calendarGridView = findViewById(R.id.calendarGridView);
        mCalendarAdapter = new CalendarAdapter(this);
        calendarGridView.setAdapter(mCalendarAdapter);
        titleText.setText(mCalendarAdapter.getTitle());


        calendarGridView.setOnItemClickListener(this);
    }
 public void listenerSet(){
        calendarGridView.setOnItemClickListener(this);
    }
}
alendarAdapter Class

public class CalendarAdapter extends BaseAdapter {
    private List<Date> dateArray = new ArrayList();
    private Context mContext;
    private DateManager mDateManager;
    private LayoutInflater mLayoutInflater;
    private String spendPrice;
    private String incomePrice;

    //カスタムセルを拡張したらここでWigetを定義
    private static class ViewHolder {
        public TextView dateText;
    }

    public CalendarAdapter(Context context){
        mContext = context;
        mLayoutInflater = LayoutInflater.from(mContext);
        mDateManager = new DateManager();
        dateArray = mDateManager.getDays();
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = mLayoutInflater.inflate(R.layout.calendar_cell, null);
            holder = new ViewHolder();
            holder.dateText = convertView.findViewById(R.id.dateText);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder)convertView.getTag();
        }

        //セルのサイズを指定
        float dp = mContext.getResources().getDisplayMetrics().density;
        AbsListView.LayoutParams params = new AbsListView.LayoutParams(parent.getWidth()/7 - (int)dp, (parent.getHeight() - (int)dp * mDateManager.getWeeks() ) / mDateManager.getWeeks());
        convertView.setLayoutParams(params);

        //その月日の金額をまとめる
        Calendar cal = (Calendar)mContext;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.JAPAN);
        String sDate = format.format(dateArray.get(position));

        String getSpend = cal.readData(sDate);
        String getIncome = cal.incomeData(sDate);

        spendPrice = "<font color=\"red\">" + getSpend + "円</font>";
        incomePrice = "<font color=\"blue\">" + getIncome + "円</font>";

        String price;

        //日付とデータベースの金額を表示させる
        SimpleDateFormat dateFormat = new SimpleDateFormat("d", Locale.JAPAN);
        if((getSpend==null) && (getIncome == null)){
            holder.dateText.setText(dateFormat.format(dateArray.get(position)));
        } else if((getSpend==null) && (getIncome!=null)){
            holder.dateText.setText(Html.fromHtml(dateFormat.format(dateArray.get(position)) + "<br>" + incomePrice));
        } else if((getSpend!=null) && (getIncome==null)) {
            holder.dateText.setText(Html.fromHtml(dateFormat.format(dateArray.get(position)) + "<br>" + spendPrice));
        } else {
            holder.dateText.setText(Html.fromHtml(dateFormat.format(dateArray.get(position)) + "<br>" + incomePrice + "<br>" + spendPrice));
        }

        //当月以外のセルをグレーアウト
        if (mDateManager.isCurrentMonth(dateArray.get(position))){
            convertView.setBackgroundColor(Color.WHITE);
        }else {
            convertView.setBackgroundColor(Color.LTGRAY);
        }

        //日曜日を赤、土曜日を青に
        int colorId;
        switch (mDateManager.getDayOfWeek(dateArray.get(position))){
            case 1:
                colorId = Color.RED;
                break;
            case 7:
                colorId = Color.BLUE;
                break;

            default:
                colorId = Color.BLACK;
                break;
        }
        holder.dateText.setTextColor(colorId);

        return convertView;
    }

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

    @Override
    public Object getItem(int position) {
        return null;
    }

    //表示月を取得
    public String getTitle(){
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM", Locale.JAPAN);
        return format.format(mDateManager.mCalendar.getTime());
    }

    //翌月表示
    public void nextMonth(){
        mDateManager.nextMonth();
        dateArray = mDateManager.getDays();
        this.notifyDataSetChanged();
    }

    //前月表示
    public void prevMonth(){
        mDateManager.prevMonth();
        dateArray = mDateManager.getDays();
        this.notifyDataSetChanged();
    }
    static Date truncateTime(Date d) {
        Instant instant = d.toInstant();
        ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("Asia/Tokyo", ZoneId.SHORT_IDS));
        ZonedDateTime truncated = zonedDateTime.truncatedTo(ChronoUnit.DAYS);
        return Date.from(truncated.toInstant());
    }
}
DateManager Class

public class DateManager {
    Calendar mCalendar;

    public DateManager(){
        mCalendar = Calendar.getInstance();
    }

    //当月の要素を取得
    public List<Date> getDays(){
        //現在の状態を保持
        Date startDate = mCalendar.getTime();

        //GridViewに表示するマスの合計を計算
        int count = getWeeks() * 7 ;

        //当月のカレンダーに表示される前月分の日数を計算
        mCalendar.set(Calendar.DATE, 1);
        int dayOfWeek = mCalendar.get(Calendar.DAY_OF_WEEK) - 1;
        mCalendar.add(Calendar.DATE, -dayOfWeek);

        List<Date> days = new ArrayList<>();

        for (int i = 0; i < count; i ++){
            days.add(mCalendar.getTime());
            mCalendar.add(Calendar.DATE, 1);
        }

        //状態を復元
        mCalendar.setTime(startDate);

        return days;
    }

    //当月かどうか確認
    public boolean isCurrentMonth(Date date){
        SimpleDateFormat format = new SimpleDateFormat("yyyy.MM", Locale.JAPAN);
        String currentMonth = format.format(mCalendar.getTime());
        if (currentMonth.equals(format.format(date))){
            return true;
        }else {
            return false;
        }
    }

    //週数を取得
    public int getWeeks(){
        return mCalendar.getActualMaximum(Calendar.WEEK_OF_MONTH);
    }

    //曜日を取得
    public int getDayOfWeek(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.DAY_OF_WEEK);
    }

    //翌月へ
    public void nextMonth(){
        mCalendar.add(Calendar.MONTH, 1);
    }

    //前月へ
    public void prevMonth(){
        mCalendar.add(Calendar.MONTH, -1);
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

ざっと見た感じですが, Calendar クラスの onItemClick で, dateArray を作成する DateManager が作られていますが,

mDateManager = new DateManager(); // <- これです
dateArray = mDateManager.getDays();
String date = titleText.getText().toString().substring(0,7);
date = date + "-" + dateArray.get(position).toString().substring(8,10)

DateManager は「何月の」dateArray を生成していますか?
画面の表示が前月や次月に移動した場合に, それに追従した dateArray が生成されるようになっていますでしょうか.

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/06 08:22 編集

    回答ありがとうございます。
    前の月表示前である当月のdateArrayが生成されているように思えます。
    DateManagerのコンストラクタにあるインスタンス設定で最初の月の値がまた入っているのでしょうか?

    キャンセル

  • 2019/02/06 11:17

    どのようにするとどのようなインスタンスが作成されるのかは, 作者の Aies さんがご理解頂いていなくてはなりません.

    DateManager はどのような存在でしょうか.
    DateManager は CalendarAdapter クラスでも使われていますが, こちらは前月・次月ボタンの操作によって, メソッドを呼び出して自身の内容を更新しているようです.
    つまり DateManager は, 現在表示中の月の各日付を管理しているモノと考えられます.
    ですから, このアプリの起動時に1つだけ生成して使うことを想定したモノで, 何度も作られるモノでは無いと思われます.

    CalendarAdapter 内の DateManager のインスタンス mDateManager を使っては如何でしょう.
    CalenderAdapter のインスタンスは Calender クラスが mCalendarAdapter として持っています.
    CalendarAdapter には, 本来こういった場合に使う getItem(int position) がありますが, 現在は null を返していて使えません.
    mDateManager.getDays().get(position) を返すようにすることで, 問題の

    date = date + "-" + dateArray.get(position).toString().substring(8,10);

    という行が

    date = date + "-" + mCalendarAdapter.getItem(position).toString().substring(8,10);

    と書けるようになります.

    キャンセル

  • 2019/02/06 12:12 編集

    なお, toString は本来デバッグ用のもので, ~.toString().substring(8,10) のように解析して使用することを想定されていないものだということを念のためお伝えしておきます.
    toString の返す文字列は, 実装やバージョン, 時にはオブジェクトの内容毎にでも異なって良いことになっています. それを知らずにいますと, 後になって「何もしていないのに動かなくなった」となる場合があります.
    文字列にして一部を取り出すのでは無く, Date 型から月・日など必要なデータを一つずつ取り出して文字列化(フォーマット)するようにしたほうが良いと思います.

    キャンセル

  • 2019/02/06 14:44

    なるほど。ご指摘の通り、一つずつコメントをつけるなどして理解するようにしたら、解決し、理解も深まりました。ありがとうございました。

    結果おっしゃっていた通り、return nullをかえ、受け取るようにしたらできました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.19%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る