RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / user-216307

RareScrap's questions

Martin Hope
RareScrap
Asked: 2020-05-30 15:54:24 +0000 UTC

ItemTouchHelper 中的自定义动画

  • 3

我使用ItemTouchHelper“滑动删除”来实现该功能。我知道还有其他方法可以做到这一点,但我读到使用ItemTouchHelper是最方便和现代的,此外它不需要第三方库。

这就是我的应用程序目前的行为方式。一旦屏幕上没有剩余元素,图片就会显示在中间

在此处输入图像描述

购物车在ShoppingCartFragment.javaRecyclerView片段中实现,此片段中使用的适配器在ShoppingCartItemRecyclerViewAdapter.java中实现。抱歉,我不是在这里发布代码,而是在 github 上发布——这是由于 30,000 个字符的限制。我将我的整个项目放在StackOverflow 的一个单独的分支中,这样这段代码就不会在未来消失。

问题

  1. 为什么滑动时元素的顶部和底部有边缘?

    在此处输入图像描述

  2. 为什么在删除列表中间的元素后留下的红色背景高度整齐地降低(gif 1),而在删除元素后,如果这个元素是列表中唯一的一个,动画不会改变高度列表(gif 2)。

    动态图 1 动态图 2

  3. 如何在 UNDO 按钮出现的动画过程中去除黑色阴影(即当她从左边驶出时)?在我看来,我需要更改隐藏在 ItemTouchHelper 某处的撤消按钮外观的标准动画,但我不知道在哪里做(即哪个方法对此负责)

    在此处输入图像描述

  4. 最重要的问题- 如何为位于滑动移动的元素“后面”的对象设置自己的动画?例如,Gmail 使用完全不同的动画来显示 UNDO 按钮:

    在此处输入图像描述

我将很高兴收到任何意见、链接和建议。如果我不清楚某处,请随时提问。

android
  • 1 个回答
  • 10 Views
Martin Hope
RareScrap
Asked: 2020-04-03 15:13:36 +0000 UTC

更改片段的标记而不重新创建它

  • 0

我想这样做,以便我的片段可以更改其标记而无需重新创建自身(即无需重新创建片段并调用其构造函数)。我试过使用 ViewGroup 及其 setView() 和 removeView() 方法将一个标记替换为另一个标记,但我似乎变得更加困惑。

还有另一种方法可以解决我的问题 - 在 Activity 标记中,立即为每个标记创建两个片段,使其中一个不可见。我认为从效率和节省资源的角度来看,这不是一种有效和正确的方法。我想知道您对如何最好地解决这个问题的看法。

下面您可以看到一段代码解决了我的问题,但是重新创建了片段对象并在单击操作栏上的特定按钮之后。当您需要更改片段的标记而不重新创建它时该怎么办?

// Обработка выбора команд меню
@Override
public boolean onOptionsItemSelected(android.view.MenuItem item) {
    FragmentManager fm = getFragmentManager(); // Необходим для транзакий фрагментов: при удалении и заменене одного фрагмента другим

    // Выбор в зависимости от идентификатора MenuItem
    switch (item.getItemId()) {
        case R.id.shopping_cart:
            return true; // Событие меню обработано
        case R.id.sort:
            if (fm != null) {
                /*
                Perform the FragmentTransaction to load in the list tab content.
                Using FragmentTransaction#replace will destroy any Fragments
                currently inside R.id.fragment_content and add the new Fragment
                in its place. (с) Google doc
                */
                FragmentTransaction ft = fm.beginTransaction(); // Начало транзакции
                ( (ViewGroup) getActivity().findViewById(R.id.fragment_menu) ).removeAllViews(); // Удаляет View на экране (сам список)
                ft.remove(this); // Удаляет кнопки на палени действий (TODO: и вместе с ним сам фрагмент?)

                // Замена фрагмента
                if (currentMode == CARD_MODE) {
                    ft.replace(R.id.fragment_menu, new MenuListFragment().setArguments(PLATE_MODE));
                }else {// currentMode == PLATE_MODE
                    ft.replace(R.id.fragment_menu, new MenuListFragment().setArguments(CARD_MODE));

                }
                ft.commit(); // Завершение транзакции
            }
            return true; // Событие меню обработано
    }

    return super.onOptionsItemSelected(item); //TODO: Разобраться зачем вообще тут нужен супер
}

如果您需要其余代码和标记,这里是完整项目代码的链接:https ://github.com/RareScrap/TennoSushi/tree/master

更新 1这是两个标记的代码,将代码显示为图块和普通列表

fragment_menu_card_list.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/card_main"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     tools:context="com.webtrust.tennosushi.MenuListFragment">

     <!-- TODO: Update blank fragment layout -->

     <ListView
          android:id="@+id/cardList"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="horizontal"></ListView>
</FrameLayout>

fragment_menu_plates_list.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/plates_main"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     tools:context=".MainActivity">

     <GridView
          android:id="@+id/platesList"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:horizontalSpacing="0dp"
          android:numColumns="2"
          android:verticalSpacing="0sp" />

</RelativeLayout>

更新 2片段代码

package com.webtrust.tennosushi;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;
import android.widget.ListView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * Простой наследник класса {@link Fragment}.
 * Активити, которые содержат этот фрагмент должно реализовывать
 * интерфейс {@link MenuListFragment.OnFragmentInteractionListener}
 * для обработки событий взаимодействия между активностью и фрагментом.
 * Используйте {@link MenuListFragment#newInstance} фабричный метод для
 * создания экземпляра этого фрагмента.
 * @author RareScrap
 */
public class MenuListFragment extends Fragment {
     // Константы, определяющие режим отображения списка
     public static int CARD_MODE = 0;
     public static int PLATE_MODE = 1;

     // Закомментирован, т.к. еще не изучен
     //private OnFragmentInteractionListener mListener;

     // Список объектов MenuItem, представляющих элементы главного меню (категории блюд)
     private List<MenuItem> menuItemList = new ArrayList<>();

     // ArrayAdapter связывает объекты MenuItem с элементами ListView
     private MenuItemArrayAdapter menuItemArrayAdapter;
     private ListView menuItemListListView; // View для вывода информации в виде списка
     private GridView menuItemListGridView; // View для вывода информации в виде плиток

     private int currentMode; // Текущий режим отображения списка

     /**
      * Необходимый пустой публичный конструктор
      */
     public MenuListFragment() {
          setArguments(PLATE_MODE);// режим по умолчанию
     }

     /**
      * Метод-замена для конструктора с параметрами т.к.
      * Google ОЧЕНЬ не рекомендует иметь дополнительные конструкторы
      * во фрагментах
      *
      * @param mode Режим отображения списка
      * @return this Возвращает этот же фрагмент (нужночтобы вызывать сразу после конструктора во FragmentTransaction
      * */
     public android.support.v4.app.Fragment setArguments(int mode) {
          this.currentMode = mode; // режим по умолчанию
          return this;
     }

     /**
      * Используйте этот фабричный метод для создания новых экземпляров
      * этого фрагмента с использованием продоставленных параментров
      * (черт знает где эти "параметры", япросто перевел сгенерированный коммент)
      *
      * @return Новый объект фрагмента {@link MenuListFragment}.
      */
     // TODO: Переменуйте и измените типы и количество параметров (перевод)
     // TODO: разобраться зачем нужен этот метод
     public static MenuListFragment newInstance() {
          MenuListFragment fragment = new MenuListFragment();
          Bundle args = new Bundle();
          fragment.setArguments(args);
          return fragment;
     }

     @Override
     public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);

     }

     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          setHasOptionsMenu(true); // у фрагмента имеются команды меню

          // Inflate the layout for this fragment
          if (currentMode == CARD_MODE) {
                return inflater.inflate(R.layout.fragment_menu_card_list, container, false);
          }else { // currentMode == PLATE_MODE
                return inflater.inflate(R.layout.fragment_menu_plates_list, container, false);
          }
     }

     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
          super.onActivityCreated(savedInstanceState);

          // TODO: Лучше ли это место для установки адаптера?
          // ArrayAdapter для связывания weatherList с weatherListView
          menuItemArrayAdapter = new MenuItemArrayAdapter(getActivity(), menuItemList);
          if (currentMode == CARD_MODE) {
                menuItemListListView = (ListView) getView().findViewById(R.id.cardList);
                menuItemListListView.setAdapter(menuItemArrayAdapter);
          }else { // currentMode == PLATE_MODE
                menuItemListGridView = (GridView) getView().findViewById(R.id.platesList);
                menuItemListGridView.setAdapter(menuItemArrayAdapter);
          }

          try {
                URL url = new URL("http://192.168.1.127/index.php");

                GetDataTask getLocalDataTask = new GetDataTask();
                getLocalDataTask.execute(url);
          }
          catch (Exception e) {
                e.printStackTrace();
          }
     }

     @Override
     public void onAttach(Context context) {
          super.onAttach(context);
          /*if (context instanceof OnFragmentInteractionListener) {
                mListener = (OnFragmentInteractionListener) context;
          } else {
                throw new RuntimeException(context.toString()
                          + " must implement OnFragmentInteractionListener");
          }*/
     }

     @Override
     public void onDetach() {
          super.onDetach();
          //mListener = null;
     }

     /**
      * Отображение команд меню фрагмента
      * @param menu Меню
      * @param inflater Инфлатер для меню
      */
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
          super.onCreateOptionsMenu(menu, inflater);
          inflater.inflate(R.menu.menu_list_menu, menu);
     }

     // Обработка выбора команд меню
     @Override
     public boolean onOptionsItemSelected(android.view.MenuItem item) {
          FragmentManager fm = getFragmentManager(); // Необходим для транзакий фрагментов: при удалении и заменене одного фрагмента другим

          // Выбор в зависимости от идентификатора MenuItem
          switch (item.getItemId()) {
                case R.id.shopping_cart:
                     return true; // Событие меню обработано
                case R.id.sort:
                     if (fm != null) {
                          /*
                          Perform the FragmentTransaction to load in the list tab content.
                          Using FragmentTransaction#replace will destroy any Fragments
                          currently inside R.id.fragment_content and add the new Fragment
                          in its place. (с) Google doc
                          */
                          FragmentTransaction ft = fm.beginTransaction(); // Начало транзакции
                          ( (ViewGroup) getActivity().findViewById(R.id.fragment_menu) ).removeAllViews(); // Удаляет View на экране (сам список)
                          ft.remove(this); // Удаляет кнопки на палени действий (TODO: и вместе с ним сам фрагмент?)

                          // Замена фрагмента
                          // TODO: Сделать так, чтобы разметка фрагмента менялась без пересоздания (читай удаления) макета
                          if (currentMode == CARD_MODE) {
                                ft.replace(R.id.fragment_menu, new MenuListFragment().setArguments(PLATE_MODE));
                          }else {// currentMode == PLATE_MODE
                                ft.replace(R.id.fragment_menu, new MenuListFragment().setArguments(CARD_MODE));

                          }
                          ft.commit(); // Завершение транзакции
                     }
                     return true; // Событие меню обработано
          }

          return super.onOptionsItemSelected(item); //TODO: Разобраться зачем вообще тут нужен супер
     }

     /**
      * Этот интерфейс должно быть реализован в активити, которая содержит этот
      * фрагмент, чтобы фрагмен смог взаимодействовать с активити
      * и ,возможно, с другими фрагментами, содержащиеся в этой активити.
      * <p>
      * Подробнее смотрите в Android Training lesson: <a href=
      * "http://developer.android.com/training/basics/fragments/communicating.html"
      * >Communicating with Other Fragments</a>
      * </p>.
      */
     // Закомментирован, т.к. еще не изучен
     /*public interface OnFragmentInteractionListener {
          // TODO: Update argument type and name
          void onFragmentInteraction(Uri uri);
     }*/

     /* Обращение к REST-совместимому (якобы) веб-сервису за данными блюд и меню
     и сохранение этих данных в локальном файле HTML */

     /**
      * Внутренний класс {@link AsyncTask} для загрузки данных
      * в формате JSON.
      * @author RareScrap
      */
     private class GetDataTask extends AsyncTask<URL, Void, JSONObject> {
          /**
            * Получение данных из сети
            * @param params URL для получения JSON файла
            * @return JSON файл с категориями меню и блюдами в них
            */
          @Override
          protected JSONObject doInBackground(URL... params) {
                HttpURLConnection connection = null;

                try {
                     connection = (HttpURLConnection) params[0].openConnection(); // Для выдачи запроса достаточно открыть объект подключения
                     int response = connection.getResponseCode(); // Получить код ответа от веб-сервера

                     if (response == HttpURLConnection.HTTP_OK) {
                          StringBuilder builder = new StringBuilder();

                          try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
                                String line;
                                while ((line = reader.readLine()) != null) {
                                     builder.append(line);
                                }
                          }
                          catch (IOException e) {
                                e.printStackTrace();
                          }

                          return new JSONObject(builder.toString());
                     }else {
                     }
                }
                catch (Exception e) {
                     e.printStackTrace();
                }
                finally {
                     connection.disconnect(); // Закрыть HttpURLConnection
                }

                return null;
          }
          /**
            * Обработка ответа JSON и обновление ListView.
            *
            * @param jsonObject JSON файл полученный после завершения работы doInBackground()
            */
          @Override
          protected void onPostExecute(JSONObject jsonObject) {
                if (jsonObject != null) {
                     convertJSONtoArrayList(jsonObject); // Заполнение weatherList
                     menuItemArrayAdapter.notifyDataSetChanged(); // Связать с ListView

                     // Прокрутить до верха
                     if (currentMode == CARD_MODE) {
                          menuItemListListView.smoothScrollToPosition(0);
                     }else { // currentMode == PLATE_MODE
                          menuItemListGridView.smoothScrollToPosition(0);
                     }
                }
          }
     }

     /**
      * Создание объектов MenuItem на базе JSONObject
      * с последующим их заесением в menuItemList.
      *
      * @param jsonObject Входящий JSON файл
      */
     private void convertJSONtoArrayList(JSONObject jsonObject) {
          menuItemList.clear(); // Стирание старых погодных данных

          try {
                // Получение свойства "list" JSONArray
                JSONArray list = jsonObject.getJSONArray("sushi");

                // Преобразовать каждый элемент списка в объект Weather
                for (int i = 0; i < list.length(); ++i) {
                     JSONObject deash = list.getJSONObject(i); // Данные за день
                     // Получить JSONObject с температурами дня ("temp")
                     String name = deash.getString("name");

                     // Получить JSONObject c описанием и значком ("weather")
                     String picURL = deash.getString("picURL");

                     // Добавить новый объект Weather в weatherList
                     menuItemList.add( new MenuItem(name, picURL));
                }
          }
          catch (JSONException e) {
                e.printStackTrace();
          }
     }
}
android
  • 1 个回答
  • 10 Views
Martin Hope
RareScrap
Asked: 2020-03-29 10:00:23 +0000 UTC

Java renameTo - 重命名不起作用

  • 1

下午好。我只是不知道如何在 Java 中重命名文件,因为 我无法跟踪此方法的操作(我使用调试器遍历了 renameTo - 我什至不知道要抓住什么,因为调试器根本无法进一步工作并大喊“找不到本机方法”) .

有趣的是,renameTo 不会在重命名失败时抛出异常,所以我完全解除了武装,不知道该怎么做。我怀疑 Java 无法重命名该文件,因为 FileWriter 没有关闭,没有写入目录的权限,或者文件正在被另一个进程使用。如何检查我的怀疑 - 我不知道,但在代码中我绝对(在我看来)关闭了 FileWriter。而关于权利缺乏的猜测消失了,因为。createNewFile() 创建文件没有任何问题。

怎样成为?希望得到您的帮助并提前致谢:)

编码:

package demon;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.io.File;
import java.io.*;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Демон, который периодически записывает в файл
 * timer.txt системную дату и время и произвольную
 * метаинформацию о системе. Если размер файла
 * превышается 100КБ, то timer.txt переименовывается
 * в timer_<datatime>.txt и создается новый файл timer.txt
 * 
 * @author Valentina
 */
public class Demon {
    private static String fileName = "C:\\Users\\rares\\Downloads\\Demon\\time.txt"; // Путь к текущему файлу
    static DateFormat formatter = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); // Формат времени
    static Date now = new Date(); 

    /**
     * Точка входа приложения
     * 
     * @param args Массив аргументов
     */
    public static void main(String[] args)  {
        Timer timer = new Timer();

        // Внутренний класс, реализующий запуск и работу демона по таймеру
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                String newfile = "C:\\Users\\rares\\Downloads\\Demon\\timer_"+formatter.format(now.getTime())+".txt";

                File file = new File(fileName);
                File file1 = new File(newfile);

                create(file);     
                long S = length(file);  
                System.out.println(S);
                if (S>20000)  {
                    if (file.renameTo(file1))
                        System.out.print("1");
                    else
                        System.out.print("0");

                    create( new File(fileName));
                }else {
                    update();
                }  
            }   
        };

        timer.schedule(task,10,10);
    } 

    /**
     * Создает новый файл с проверкой на существование
     * @param file Входной объект класса {@link File}, который будет создан
     */
    public static void create(File file){
        if(file.exists())
            System.out.println("Файл существует");
        else
            try {
                file.createNewFile(); 
            } catch (IOException ex) {}
    }

    /**
     * Записывает в файл одну строку, состоящую из времени и имени пользователя
     */
    public static void update() { // TODO: Имхо, это не должно быть статиком.
        File file = new File(fileName);
        FileWriter fr;

        String s = formatter.format(now.getTime());
        System.out.println(s);

        try {
            fr = new FileWriter (file,true);
            s+=System.getProperty("user.name");
            fr.write(s);
            fr.close();
        } catch (IOException e) {
            e.printStackTrace();
            try {
                fr.close();
            } catch (IOException ex) {
                Logger.getLogger(Demon.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * Метод-обертка, возаращающий размер файла
     * @param file Объект {@link File}, для которого нужно получить размер
     * @return Размер файла в битах
     */
    public static Long length(File file) {     
        return file.length();
    }
}
java
  • 2 个回答
  • 10 Views
Martin Hope
RareScrap
Asked: 2020-03-23 17:33:42 +0000 UTC

只有一个元素时如何使用layout_weight(相对布局题)

  • 0

下午好。有标记

<ImageView
    android:id="@+id/imageView4"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    myapp:srcCompat="@mipmap/ic_launcher"
    android:layout_weight="9" />

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_gravity="center_horizontal"
    android:layout_weight="1"
    android:orientation="horizontal">

    <Button
        android:id="@+id/button"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="OK" />

    <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="Отмена" />
</LinearLayout>

此布局使用 layout_weight 在 90%(图片)和 10%(按钮栏)之间拆分屏幕。我在 css 中使用 layout_weight 之类的百分比

如何使 10% 的屏幕高度仍然分配在带有按钮的面板下,但同时使 ImageView 不出现在布局中(如果从上面的标记中删除它,则面板带有按钮将占据整个屏幕)? 在此处输入图像描述

为什么我需要这个:我想为我的列表 制作这样一个元素在此处输入图像描述

在我看来,这件事是这样完成的:图片被拉伸到屏幕的宽度和高度(不要忘记我们只是从列表中输入ELEMENT),从下面添加一个 FrameLayout,其中一个透明图片设置为背景参数。然后将 TextView 放在 FrameLayout 上

在此处输入图像描述

我不想以特定数字设置 FrameLayout 的高度,因为我认为这会影响应用程序在小屏幕上的外观。而且我一开始用的方法也不合适,因为。图片也显示在 FrameLayout 下。当上面没有元素时,如何将 FrameLayout 的高度设置为百分比?您如何看待这种方法?我是设计和布局的新手,所以我很想听听您的意见和建议。

提前致谢!

android
  • 3 个回答
  • 10 Views
Martin Hope
RareScrap
Asked: 2020-03-21 13:45:16 +0000 UTC

不从 MainActivity 调用 GetView 适配器方法

  • 0

下午好。我正在学习为 android 编写应用程序,在其中一本书中我找到了一本培训手册——创建天气预报应用程序。我遵循了手册,但最终注意到以下内容:我的应用程序处理获取 json 文件、解析它并创建具有所需值的列表元素的任务,但适配器不会以任何方式显示我的列表元素。我使用调试器检查了项目中的所有方法,并注意到适配器的 getView 方法从未被调用过。我将它与我应该拥有的应用程序进行了多次比较,但没有发现显着差异。

单击按钮后应用程序应如何运行:

在此处输入图像描述

在我的案例中它的表现如何:

在此处输入图像描述

代码:MainActivity.java:

package ru.rarescrap.educationweatherview;

// MainActivity.java
// Вывод 16-дневного прогноза погоды для заданного города

import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.inputmethod.InputMethodManager; // Для сокрытия клавиатуры по нажатию FAB
import android.widget.EditText;
import android.widget.ListView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
    // Список объектов Weather, представляющих прогноз погоды
    private List<Weather> weatherList = new ArrayList<>();

    // ArrayAdapter связывает объекты Weather с элементами ListView
    private WeatherArrayAdapter weatherArrayAdapter;
    private ListView weatherListView; // Для вывода информации

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Сгенерированный код для заполнения макета и настройки Toolbar
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // ArrayAdapter для связывания weatherList с weatherListView
        weatherListView = (ListView) findViewById(R.id.weatherListView);
        weatherArrayAdapter = new WeatherArrayAdapter(this, weatherList);
        weatherListView.setAdapter(weatherArrayAdapter);

        // FAB скрывает клавиатуру и выдает запрос к веб-сервису
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Получить текст из locationEditText и создать URL веб-сервисы
                EditText locationEditText = (EditText) findViewById(R.id.locationEditText);
                URL url = createURL(locationEditText.getText().toString());

                // Скрыть клавиатуру и запустить GetWeatherTask для получения
                // погодных данных от OpenWeatherMap.org в отдельном потоке
                if (url != null) {
                    dismissKeyboard(locationEditText);
                    GetWeatherTask getLocalWeatherTask = new GetWeatherTask();
                    getLocalWeatherTask.execute(url);
                }else {
                    Snackbar.make(findViewById(R.id.coordinatorLayout),
                            R.string.invalid_url, Snackbar.LENGTH_LONG).show();
                }
            }
        });
    }

    // Клавиатура закрывается при касании кнопки FAB
    private void dismissKeyboard(View view) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); // Этот метод может возвращать объекты многих разных типов, поэтому возвращаемое значение необходимо преобразовать к нужному типу
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }

    /***
     * Создание URL веб-сервисы openweathermap.org для названия города
     * @param city - Город, для которого ищется погодный прогноз
     * @return URL
     */
    private URL createURL(String city) {
        String apiKey = getString(R.string.api_key);
        String baseUrl = getString(R.string.web_service_url);

        try {
            // Создание URL для заданного города и температурной шкалы (Фаренгейт)
            /* Параметр units может принимать значения imperial (для шкалы Фаренгейта),
            metric (для шкалы Цельсия) или standard (для шкалы Кельвина) — если параметр
            units не указан, по умолчанию используется значение standard. Параметр cnt
            определяет количество дней в прогнозе. Максимальное значение равно 16,
            значение по умолчанию равно 7 (при некорректном количестве дней возвращается
            прогноз на 7 дней). По умолчанию прогноз возвращается в формате JSON, хотя
            вы можете добавить параметр mode со значением XML или HTML, чтобы получить
            данные в формате XML или веб-страницы соответственно.*/
            String urlString = baseUrl + URLEncoder.encode(city, "UTF-8") + "&units=imperial&cnt=16&APPID=" + apiKey;
            return new URL(urlString);
        }
        catch (Exception e) {
            e.printStackTrace();
        }

        return null; // Некорректный URL
    }

    /* Обращение к REST-совместимому веб-сервису за погодными данными
    и сохранение данных в локальном файле HTML */
    private class GetWeatherTask
    extends AsyncTask<URL, Void, JSONObject> {
        @Override
        protected JSONObject doInBackground(URL... params) {
            HttpURLConnection connection = null;

            try {
                connection = (HttpURLConnection) params[0].openConnection(); // Для выдачи запроса достаточно открыть объект подключения
                int response = connection.getResponseCode(); // Получить код ответа от веб-сервера

                if (response == HttpURLConnection.HTTP_OK) {
                    StringBuilder builder = new StringBuilder();

                    try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
                        String line;
                        while ((line = reader.readLine()) != null) {
                            builder.append(line);
                        }
                    }
                    catch (IOException e) {
                        Snackbar.make(findViewById(R.id.coordinatorLayout),
                                R.string.read_error, Snackbar.LENGTH_LONG).show();
                        e.printStackTrace();
                    }

                    connection.disconnect();
                    return new JSONObject(builder.toString());
                }else {
                    Snackbar.make(findViewById(R.id.coordinatorLayout),
                            R.string.connect_error, Snackbar.LENGTH_LONG).show();
                }
            }
            catch (Exception e) {
                Snackbar.make(findViewById(R.id.coordinatorLayout),
                        R.string.connect_error, Snackbar.LENGTH_LONG).show();
                e.printStackTrace();
            }
            finally {
                connection.disconnect(); // Закрыть HttpURLConnection
            }

            return null;
        }

        // Обработка ответа JSON и обновление ListView
        @Override
        protected void onPostExecute(JSONObject weather) {
            if (weather != null) {
                convertJSONtoArrayList(weather); // Заполнение weatherList
                weatherArrayAdapter.notifyDataSetChanged(); // Связать с ListView
                weatherListView.smoothScrollToPosition(0); // Прокрутить до верха
            }
        }
    }

    // Создание объектов Weather на базе JSONObject с прогнозом
    private void convertJSONtoArrayList(JSONObject forecast) {
        weatherList.clear(); // Стирание старых погодных данных

        try {
            // Получение свойства "list" JSONArray
            JSONArray list = forecast.getJSONArray("list");

            // Преобразовать каждый элемент списка в объект Weather
            for (int i = 0; i < list.length(); ++i) {
                JSONObject day = list.getJSONObject(i); // Данные за день
                // Получить JSONObject с температурами дня ("temp")
                JSONObject temperatures = day.getJSONObject("temp");

                // Получить JSONObject c описанием и значком ("weather")
                JSONObject weather = day.getJSONArray("weather").getJSONObject(0);

                // Добавить новый объект Weather в weatherList
                weatherList.add(new Weather(
                        day.getLong("dt"), // Временная метка даты/времени
                        temperatures.getDouble("min"), // Мин. температура
                        temperatures.getDouble("max"), // Макс. температура
                        day.getDouble("humidity"), // Процент влажности
                        weather.getString("description"), // Погодные условия
                        weather.getString("icon"))); // Имя значка
            }
        }
        catch (JSONException e) {
            e.printStackTrace();
        }
    }
}

WeatherArrayAdapter.java:

// Объект ArrayAdapter для отображения элементов List<Weather> в ListView
package ru.rarescrap.educationweatherview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter; // Родительский класс
import android.widget.ImageView;
import android.widget.TextView;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class WeatherArrayAdapter extends ArrayAdapter<Weather> {
    // Класс для повторного использования представлений списка при прокрутке
    private static class ViewHolder {
        ImageView conditionImageView;
        TextView dayTextView;
        TextView lowTextView;
        TextView hiTextView;
        TextView humidityTextView;
    }

    // Кэш для уже загруженных объектов Bitmap
    private Map<String, Bitmap> bitmaps = new HashMap<>();

    // Конструктор для инициализации унаследованных членов суперкласса
    public WeatherArrayAdapter(Context context, List<Weather> forecast) {
        /*
        в первом и третьем аргументах передаются объект Context (то есть активность,
        в которой отображается ListView) и List<Weather> (список выводимых данных).
        Второй аргумент конструктора суперкласса представляет идентификатор ресурса
        макета, содержащего компонент TextView, в котором отображаются данные ListView.
        Аргумент –1 означает, что в приложении используется пользовательский макет,
        чтобы элемент списка не ограничивался одним компонентом TextView.
         */
        super(context, -1, forecast);
    }

    // Создание пользовательских представлений для элементов ListView
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Получение объекта Weather для заданной позиции ListView
        Weather day = getItem(position);

        //Объект, содержащий ссылки на представления элемента списка
        ViewHolder viewHolder;

        // Проверить возможность повторного использования ViewHolder для элемента, вышедшего за границы экрана
        if (convertView == null) { // Объекта ViewHolder нет, создать его
            viewHolder = new ViewHolder();
            LayoutInflater inflater = LayoutInflater.from(getContext());
            convertView = inflater.inflate(R.layout.list_item, parent, false); // последнем аргументе передается флаг автоматического присоединения представлений
            viewHolder.conditionImageView = (ImageView) convertView.findViewById(R.id.conditionImageView);
            viewHolder.dayTextView = (TextView) convertView.findViewById(R.id.dayTextView);
            viewHolder.lowTextView = (TextView) convertView.findViewById(R.id.lowTextView);
            viewHolder.hiTextView = (TextView) convertView.findViewById(R.id.hiTextView);
            viewHolder.humidityTextView = (TextView) convertView.findViewById(R.id.humidityTextView);
            convertView.setTag(viewHolder);
        }else { // Cуществующий объект ViewHolder используется заново
            viewHolder = (ViewHolder) convertView.getTag();
        }

        // Если значок погодных условий уже загружен, использовать его;
        // в противном случае загрузить в отдельном потоке
        if (bitmaps.containsKey(day.iconURL)) {
            viewHolder.conditionImageView.setImageBitmap(
            bitmaps.get(day.iconURL));
        }else {
            // Загрузить и вывести значок погодных условий
            new LoadImageTask(viewHolder.conditionImageView).execute(day.iconURL);
        }

        // Получить данные из объекта Weather и заполнить представления
        Context context = getContext(); // Для загрузки строковых ресурсов
        // Назначается текст компонентов TextView элемента ListView
        viewHolder.dayTextView.setText(context.getString(R.string.day_description, day.dayOfWeek, day.description)); // Первый аргумент - строка; Второй - аргументы для форматирования
        viewHolder.lowTextView.setText(context.getString(R.string.low_temp, day.minTemp));
        viewHolder.hiTextView.setText(context.getString(R.string.high_temp, day.maxTemp));
        viewHolder.humidityTextView.setText(context.getString(R.string.humidity, day.humidity));

        return convertView; // Вернуть готовое представление элемента
    }

    // TODO: Как полученное изображение присваивается viewHolder и представлению?
    // Кажись, изменение imageView так же изменяет и аргумент, переданный в конструкторе LoadImageTask(). Таким образом, создается нечно вроде "ссылки"
    // AsyncTask для загрузки изображения в отдельном потоке
    private class LoadImageTask extends AsyncTask<String, Void, Bitmap> {
        private ImageView imageView; // Для вывода миниатюры

        // Сохранение ImageView для загруженного объекта Bitmap
        public LoadImageTask(ImageView imageView) {
            this.imageView = imageView;
        }

        // загрузить изображение; params[0] содержит URL-адрес изображения
        @Override
        protected Bitmap doInBackground(String... params) {
            Bitmap bitmap = null;
            HttpURLConnection connection = null;

            try {
                URL url = new URL(params[0]); // Создать URL для изображения

                // Открыть объект HttpURLConnection, получить InputStream
                // и загрузить изображение
                connection = (HttpURLConnection) url.openConnection(); // Преобразование типа необходимо, потому что метод возвращает URLConnection

                try (InputStream inputStream = connection.getInputStream()) {
                    bitmap = BitmapFactory.decodeStream(inputStream);
                    bitmaps.put(params[0], bitmap); // Кэширование
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
            finally { // Этот участок кода будет выполняться независимо от того, какие исключения были возбуждены и перехвачены
                connection.disconnect(); // Закрыть HttpURLConnection
            }

            return bitmap;
        }

        // Связать значок погодных условий с элементом списка
        // Выполняется в потоке GUI вроде как для вывода изображения
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            imageView.setImageBitmap(bitmap);
        }
    }
}

天气.java:

package ru.rarescrap.educationweatherview;

// Используются для преобразования временной метки каждого дня в название дня недели
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;

class Weather {
    // объекты String в Java неизменяемы (immutable), поэтому несмотря на такое объявление, их значения измениться не могут
    public final String dayOfWeek;
    public final String minTemp;
    public final String maxTemp;
    public final String humidity;
    public final String description;
    public final String iconURL;

    // Конструктор
    public Weather(long timeStamp, double minTemp, double maxTemp, double humidity, String description, String iconName) {
        // NumberFormat для форматирования температур в целое число
        NumberFormat numberFormat = NumberFormat.getInstance();
        numberFormat.setMaximumFractionDigits(0); // Запрещает числа почле запятой

        this.dayOfWeek = convertTimeStampToDay(timeStamp); // Получение названия дня недели и инициализации dayOfWeek
        this.minTemp = numberFormat.format(minTemp) + "\u00B0F"; // Минимальная температура по Фаренгейту
        this.maxTemp = numberFormat.format(maxTemp) + "\u00B0F"; // Максимальная температура по Фаренгейту
        this.humidity = NumberFormat.getPercentInstance().format(humidity / 100.0);
        this.description = description; // Инициализирует описание погодных условий
        this.iconURL = "http://openweathermap.org/img/w/" + iconName + ".png"; // Изображение погодных условий для погоды
    }

    // Преобразование временной метки в название дня недели (Monday, ...)
    private static String convertTimeStampToDay(long timeStamp) {
        Calendar calendar = Calendar.getInstance(); // Объект Calendar
        calendar.setTimeInMillis(timeStamp * 1000); // Получение времени
        TimeZone tz = TimeZone.getDefault(); // Часовой пояс устройства

        // Поправка на часовой пояс устройства
        calendar.add(Calendar.MILLISECOND, tz.getOffset( calendar.getTimeInMillis() ));

        // Объект SimpleDateFormat, возвращающий название дня недели
        SimpleDateFormat dateFormatter = new SimpleDateFormat("EEEE"); // EEEE - первый четыре буквы для недели
        return dateFormatter.format(calendar.getTime());
    }
}

Если вам не удобно читать код тут, я выложил его на гитхаб для вас: https://github.com/RareScrap/EducationWeatherView/tree/for_stackOverFlw

Всю голову уже сломал над этой проблемой. Заранее спасибо!

UPDATE 1: Если верить этому треду, то проблем причин у нас может быть несколько

  1. Адаптер теряет ссылку на список. Я прошелся отладчиком по всей проге и этот вариант отпадает. На скрине ниже показывается одинаковый номер в объекте листа и листа адаптера (насколько я могу судить, это означает что ссылка одна и та же) 在此处输入图像描述

  2. В адаптер постоянно добавляется новый список. Понятие не имею как это мешает моей задаче. буду благодарен за объяснения.

  3. Данные добавляются в адаптер до их фактического добавления в лист. Инфа 100, что такого нет. Потому что я просто не могу увидеть это :Р

UPDATE 2 Предпринял радикальную меру: взял код из мануала и просто копипастнул в моей проект - не заработало. Очевидно, что ошибка не в коде, т.к. пример из мануала работает как нужно. У проблемы ноги растут из Gradle'а! Готов поспорить что при переходе на новую версию градла некоторые вещи перестали работать. Завтра попробую покопаться откатить градл-скрипт и посмотрим что из этого выйдет. Это не первый раз, когда из-за апдейта градла у меня вылезают непредсказуемые ошибки. Где хоть найти чейжлог с изменениями при каждом обновлении?

UPDATE 3: С откаченным градлом все работает. Вот только пространство имен app в xml разметках более не работает.

UPDATE 4: Проблема была в разметке.

java
  • 2 个回答
  • 10 Views
Martin Hope
RareScrap
Asked: 2020-03-10 16:40:35 +0000 UTC

无法在 GridLayout 中调整 ImageButton 的大小

  • 0

下午好。我正在标记一个列表项。我只是无法获得按钮的大小(20dp x 20dp)。如果可以在高度上设置它,那么在宽度上就不可能了。似乎是永远的挂在那里wrap_content。

标记本身:

    <LinearLayout 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:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".layout.SushiMenu">

    <ImageView
        android:layout_height="wrap_content"
        android:id="@+id/sushiImageView"
        app:srcCompat="@mipmap/sushi_test_image"
        android:layout_width="@dimen/item_picture_width"
        android:scaleType="fitCenter" />

    <GridLayout
        android:orientation="vertical"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:useDefaultMargins="true"
        android:columnCount="2"
        android:rowCount="3"
        android:layout_weight="1">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/name"
            android:layout_column="0"
            android:layout_row="0"
            android:text="New Text"
            android:layout_columnWeight="1" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/price"
            android:layout_column="1"
            android:layout_row="0"
            android:text="New Text"
            android:layout_columnWeight="1"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:id="@+id/components"
            android:layout_column="0"
            android:text="LargeText"
            android:layout_columnSpan="2"/>


        <ImageButton
            android:layout_width="wrap_content"
            app:srcCompat="@android:drawable/ic_input_add"
            android:id="@+id/imageButton"
            android:layout_column="1"
            android:layout_row="2"
            android:layout_columnWeight="1"
            android:layout_height="20dp"/>


    </GridLayout>
</LinearLayout>

证明,ImageButton 绝对不关心layout_width: 在此处输入图像描述 在此处输入图像描述

请帮助理解。我只是不能在右边做一个小按钮:)。

android
  • 1 个回答
  • 10 Views
Martin Hope
RareScrap
Asked: 2020-02-01 07:31:54 +0000 UTC

NetBeans 中的 Github Gist——就像在 Android Studio 中一样

  • 0

我经常使用 Android Studio 中的要点创建功能,并希望在 NetBeans 中拥有相同的功能。

在此处输入图像描述

是否有任何插件或其他方式来做到这一点?(我没找到)

如果没有,是否有其他支持 gist 的 PHP 开发 IDE?

php
  • 1 个回答
  • 10 Views
Martin Hope
RareScrap
Asked: 2020-08-08 10:41:06 +0000 UTC

在嵌套类的情况下构建 set 和 get 方法的正确方法

  • 0

你好。我无法决定 Property 类的 set 和 get 方法的定义。

UML 图

Property 类是必须写入文件的键/值对的实现。这对看起来像这样 - Key: value1, value2, ...

PopertyView 负责将属性写入文件的方式。

PropertyType 表示“键”。

PropertyValue 是一个“值”。

PropertyType 和 PropertyValue 实现了许多不同的设置方法,但为了使用它们更改 Property 类的键或值字段,get 方法需要返回对这些字段的引用。但是 set 和 get 的一般目的是保护数据免受不必要的更改。既然我将使用 get 方法返回一个链接,那么 set 和 get 方法有什么意义呢?然后更容易公开这些字段。

如果您拒绝返回对 get 方法的引用,那么从 PropertyType 和 PropertyValue 类调用 set 方法的唯一方法是在 Property 中创建它们的对应方法,但是如果有更多的类,这可能会变成整个 set 方法的动物园.

遇到这种情况怎么办?

ооп
  • 2 个回答
  • 10 Views
Martin Hope
RareScrap
Asked: 2020-08-03 07:36:43 +0000 UTC

什么时候不应该使用 SOLID?

  • 28

我读到 SOLID 是很好的建议,经过时间考验,但你不应该将它们强加到每个项目中。在没有必要使用它并且没有找到它的情况下搜索示例。告诉我,在什么情况下使用 SOLID 是不合理的?

ооп
  • 2 个回答
  • 10 Views
Martin Hope
RareScrap
Asked: 2020-07-29 11:58:51 +0000 UTC

为什么 GridLayout 需要 orientation 属性?

  • 0

文档说这个属性没有在 GridLayout 中使用,但首先它在那里,其次,改变它对嵌套元素有影响:

垂直方向 在此处输入图像描述

为什么会这样?

android
  • 1 个回答
  • 10 Views

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5