Ответы пользователя по тегу Android
  • Как объединить несколько файлов в один?

    @terminator-light
    Декомпозируйте задачу:
    1. создаёте строку
    2. гуглите, как читать файл
    3. читаете файл
    4. содержимое файла джойните к стрингу
    5. и так 3-4 пункты повторяете в цикле
    6. как закончили, гуглите, как записать в файл
    7. стринг сохраняете в новый файл и записываете

    И все, профит...
    Также желательно всю работу делать в отдельном потоке
    Ответ написан
    Комментировать
  • Как хранить данные во время работы android приложения?

    @terminator-light
    приложение рекомендуется разделить на несколько слоев, например, в архитектуре MVP на Model View Presenter.
    View - слой для управления визуальной частью приложения, это могут быть классы Activity, Fragment..
    Presenter - слой, отвечающий за обработку событий, слой методы которого вызывает View,
    Model - слой данных, именно тут вы храните свои данные. Presenter достает данные из этого слоя и вызывает методы View для отображения полученных данных.
    public interface WeatherDataView{
          void showWeatherData(WeatherData data);
    }
    public class WeatherActivity extends AppCompatActivity implements WeatherDataView{
           private WeatherPresenter presenter;
           ....
           public void onCreate(){
                  ...
                  presenter.loadWeatherData();
                  ...
           }
    
            public void showWeatherData(WeatherData data){
                ....
            }
    }
    public class WeatherPresenter  {
          private Repository repository;
          private WeatherDataView view;
          ...
          public void loadWeatherData(){
                WeatherData data = repository.fetchWeatherData(); 
                view.showWeatherData(data);
          }
    }
    //это в слое Model
    public class Repository{
          public WeatherData fetchWeatherData(){
               return api.fetchWeatherData();
          }
    }

    Пример слишком упрощенный
    Ответ написан
    Комментировать
  • Kotlin + Android studio?

    @terminator-light
    Зачем переделывать, если Kotlin интероперабелен с Java? Их вполне можно юзать в одном проекте вместе.
    Перевести весь проект на Kotlin - это уже зависит от предпочтений, вкуса, времени самого программиста/бизнеса
    Ответ написан
    Комментировать
  • Книга The Busy Coder’s Guide to Android Development имеет недостатки для обучения?

    @terminator-light
    Книга достаточно старая, версия Android до 3.0. Масса проблем и их решений, которые были под те старые устройства, сейчас не актуальны. Я бы посоветовал эти книги:
    1. Head First. Программирование для Android
    2. Android. Программирование для профессионалов 3-e издание 2017
    3. Клифтон Ян - Проектирование пользовательского интерфейса в Android. 2-е издание - 2017
    4. Reactive Programming with RxJava

    Нормально ли изучать android разработку по startandroid?
    а так читайте https://developer.android.com/
    https://medium.com/androiddevelopers
    и смотрите https://www.youtube.com/channel/UCVHFbqXqoYvEWM1Dd...
    Ответ написан
  • Программирование под android на слабом ноуте?

    @terminator-light
    Популярных IDE для разработки под Android 2: Android Studio и Eclipse. Но в основном все используют AS.
    Мне 6GB RAM не хватает, а кому-то, читал, и 16, Android Studio - очень прожорливая. В любом случае, с 16GB будет комфортнее работать
    UPD
    Если вы имеете в виду, программирование, т.е. изучение, а не профессиональная разработка, то подойдет обычный текстовый редактор + система сборки Gradle. И больше тяжеловесное, врядли сможете запустить еще
    Ответ написан
  • Почему получают доступ к контексту через MainActivity.this?

    @terminator-light
    В этом и заключается суть наслеования. Если класс B наследуется от A, то A - это родитель класса B, а B - потомок A. Пускай даже если C будет наследоваться от B, то он не перестанет быть потомком класса A, просто для класса B он будет прямым потомком.
    Если какой-то метод требует объект класса Context, то можно будет передавать любые потомки класса Context. А MainActivity - потомок Context, поэтому он подойдет.
    Я не получил ссылку ни на внешний класс, не на класс родитель.

    class C extends B{
    
        class D {
            public void method(){
                System.out.println(C.this.toString()); // C.this - ссылка на внешний класс, т.е. на C
                System.out.println(super.toString()); // super или D.super - ссылка на родительский класс,
                // т.е. на Object, т.к. по умолчанию все классы наследуются от Object
            }
    
            @Override
            public String toString() {
                return "D";
            }
        }
    
        @Override
        public String toString() {
            return "C";
        }
    }
    Ответ написан
    Комментировать
  • Как установить setOnItemLongClickListener для элементов spinera?

    @terminator-light
    Он отнаследовался от Spinner, потому что метод onDetachedFromWindow() protected, а значит, он не доступен извне, поэтому он сделал его public. Если не вызвать данный метод, дропдаун не закроется после клика на итем. Если не хотите наследоваться, можно воспользоваться рефлексией как здесь https://stackoverflow.com/a/35759475
    Приведенный код работает:
    SpinnerAdapter
    public class SpinnerAdapter extends BaseAdapter {
    
        private List<String> items;
        private LayoutInflater inflater;
        private ItemClickListener clickListener;
    
        public SpinnerAdapter(Context context, List<String> items, ItemClickListener clickListener){
            inflater = LayoutInflater.from(context);
            this.items = items;
            this.clickListener = clickListener;
        }
    
        @Override
        public int getCount() {
            return items.size();
        }
    
        @Override
        public Object getItem(int position) {
            return items.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        public View getView(int position, View convertView, ViewGroup parent) {
            if(convertView == null) {
                convertView = inflater.inflate(R.layout.spinner_item, parent, false);
            }
            ((TextView)convertView).setText(items.get(position));
            convertView.setTag(position);
            convertView.setClickable(false);
            convertView.setLongClickable(false);
            
            return convertView;
        }
    
    
        @Override
        public View getDropDownView(int position, View convertView, ViewGroup parent) {
            convertView = getView(position, convertView, parent);
            convertView.setTag(position);
    
            convertView.setOnClickListener(v -> {
                if (clickListener != null) {
                    clickListener.onItemClicked(v);
                }
            });
    
            convertView.setOnLongClickListener(v -> {
                if (clickListener != null) {
                    clickListener.onItemLongClicked(v);
                }
                return true;
            });
    
            return convertView;
        }
    }

    ItemClickListener
    public interface ItemClickListener {
        void onItemLongClicked(View view);
        void onItemClicked(View view);
    }

    MainActivity
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main_activity);
            List<String> strings = new ArrayList<String>(){{
                add("John");
                add("James");
                add("Jake");
                add("Jane");
            }};
    
            CustomSpinner spinner = findViewById(R.id.spinner);
    
            spinner.setAdapter(new SpinnerAdapter(this, strings, new ItemClickListener() {
                @Override
                public void onItemLongClicked(View view) {
                    spinner.onDetachedFromWindow();
                    final int pos = (int) view.getTag();
                    spinner.setSelection(pos);
                    Toast.makeText(MainActivity.this, "regular click: "+strings.get(pos), Toast.LENGTH_SHORT).show();
                }
    
                @Override
                public void onItemClicked(View view) {
                    spinner.onDetachedFromWindow();
                    final int pos = (int) view.getTag();
                    spinner.setSelection(pos);
                    Toast.makeText(MainActivity.this, "long click: "+strings.get(pos), Toast.LENGTH_SHORT).show();
                }
    
            }));
        }
    }

    spinner_item.xml
    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:layout_height="wrap_content">
    </TextView>
    Ответ написан
    1 комментарий
  • Как сделать recycler view слайдер?

    @terminator-light
    тут есть 2 варианта: либо использовать RecyclerView и приаттачить к нему PagerSnapHelper,
    либо ViewPager2
    UPD:
    layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
        recyclerView.setLayoutManager(layoutManager);
        PagerSnapHelper snapHelper = new PagerSnapHelper();
        snapHelper.attachToRecyclerView(recyclerView);
    Ответ написан
    4 комментария
  • Android development: каким должен быть масштабируемый код?

    @terminator-light
    Для того чтобы писать масштабируемый необходимо знание не только паттернов GOF,
    но и архитектурных: MVC, MVP, MVVM, MVI. Важно изучить достоинства и недостатки каждого.
    Во главу угла также стоят принципы SOLID, далее приведу небольшие примеры:
    1. Single Responsibility principle - принцип единственной ответственности. Метод/класс должен выполнять только одну задачу. Например, метод, предназначенный для загрузки данных из сети, не должен заниматься обработкой ошибок.
    Псевдокодом напишу:
    spoiler
    public void loadData(String url){
    	repository.fetchProducts().get(products->{
    		view.showProducts(products);
    	}, throwable ->{
    		if(throwable instanceof IOexception){
    			view.showNoNetwork();
    		}else(throwable instanceof HTTPException){
    			HTTPException exception = (HTTPException)throwable;
    		 	switch(exception.getCode()){
    		 		case 400:
    		 			view.showError(exception.getMessage());
    		 			break;
    		 		case 401:
    		 			view.showUnauthorized();
    		 			break;
    		 			...
    		 	}
    		}
    		...
    	});
    }

    Вместо этого вторую часть нужно выделить в другой метод/класс.
    spoiler
    public void loadData(String url){
    	repository.fetchProducts().get(products-> view.showProducts(products), 
    		throwable -> ErrorUtil.handleError(throwable, view));
    }
    
    public class ErrorUtil{
    	public static void handleError(Throwable throwable, View view){
    		if(throwable instanceof IOexception){
    			view.showNoNetwork();
    		}else(throwable instanceof HTTPException){
    			HTTPException exception = (HTTPException)throwable;
    		 	switch(exception.getCode()){
    		 		case 400:
    		 			view.showError(exception.getMessage());
    		 			break;
    		 		case 401:
    		 			view.showUnauthorized();
    		 			break;
    		 			...
    		 	}
    		}
    		...
    	}
    }


    2. Open/Closed principle - принцип открытости/закрытости. Код должен быть открыт для добавления функциональности, но закрыт для изменения.
    Например, есть такой код для работы с тулбаром. Если экранов будет много с разными тулбарами,
    то постоянно придется добавлять новую ветку case, а значит изменять класс ToolbarManager,
    при этом есть возможность появления ошибки в местах, касающихся и других case-веток
    spoiler
    public class ToolbarManager{
    	public void showToolbar(int type){
    		switch(type){
    			case MAIN:
    				....
    				//огромный кусок кода для показа тулбара для главного экрана
    				....
    				break;
    			case PROFILE:
    				...
    				//огромный кусок кода для показа тулбара для экрана профиля
    				...
    			...
    		}
    	}
    }

    Решение: воспользоваться одним из принципов ООП - полиморфизмом. Теперь, если понадобится добавить
    новый экран, нужно будет просто реализовать интерфейс, и это не будет касаться кода других экранов.
    spoiler
    public interface ToolbarManager{
    	void showToolbar();
    }
    
    public class MainToolbarManager implements ToolbarManager{
    	public void showToolbar(){
    		....
    		//огромный кусок кода для показа тулбара для главного экрана
    		....
    	}
    }
    
    public class ProfileToolbarManager implements ToolbarManager{
    	public void showToolbar(){
    		....
    		//огромный кусок кода для показа тулбара для экрана профиля
    		....
    	}
    }

    3. Liskov Substitution principle - принцип подстановки Барбары Лисков гласит: Если класс B - это подтип A, то мы должны иметь
    возможность заменить A на B, не нарушая поведение программы.
    Для данного принципа не могу придумать пример, связанный с Android, но мне понравился этот пример,
    взятый из этого сайта https://www.baeldung.com/solid-principles
    spoiler
    public interface Car {
        void turnOnEngine(); //запустить двигатель
        void accelerate(); //подать газ
    }
    
    public class MotorCar implements Car {
     
        private Engine engine;
     
        public void turnOnEngine() {
            //вруби мотор!
            engine.on();
        }
     
        public void accelerate() {
            //поезжай вперед!
            engine.powerOn(1000);
        }
    }

    Наш класс удовлетворяет интерфейсу, у нас есть машина, которая имеет свой мотор,
    и мы можем ускориться. Но мы живем в 2019 году, а Илон Маск старательный человек. Мы живем в эпоху электрокаров:
    spoiler
    public class ElectricCar implements Car {
     
        public void turnOnEngine() {
            throw new AssertionError("А у меня вообще нет двигателя");
        }
     
        public void accelerate() {
            //ускорение сумасшедшее!
        }
    }

    Выбрасывая машину без двигателя в общую кучу, мы меняем поведение нашей программы.
    Это грубое нарушение принципа подстановки Барбары Лисков. Решить данную проблему будет непросто.
    Но одним из решений было бы разбиение нашей модели на мелкие интерфейсы, которые учитывают состояние без двигателя
    spoiler
    public interface Engineful {
    
        void turnOnEngine();
    }
    public interface Acceleratable {
        void accelerate();
    }
    public class MotorCar implements Engineful, Acceleratable {
     
        private Engine engine;
     
        public void turnOnEngine() {
            //вруби мотор!
            engine.on();
        }
     
        public void accelerate() {
            //поезжай вперед!
            engine.powerOn(1000);
        }
    }
    public class ElectricCar implements Acceleratable {
        public void accelerate() {
            //ускорение сумасшедшее!
        }
    }


    4. Interface segregation principle - прин­цип раз­де­ле­ния интер­фейса.
    Создавайте гранённые мелкие интерфейсы. Клиентский код не должен зависеть от функций интерфейса, которые он
    не будет использовать. Возьмем пример из Android SDK, убрал некоторые детали для простоты:
    spoiler
    public interface TextWatcher{
        public void beforeTextChanged(CharSequence s, int start, int count, int after);
        public void onTextChanged(CharSequence s, int start, int before, int count);
        public void afterTextChanged(Editable s);
    }


    И клиентский код:
    spoiler
    editText.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
        }
    
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
    
        }
    
        @Override
        public void afterTextChanged(Editable s) {
        	//метод, который нам нужен
        	//какие-то полезные действия
        }
    });

    Как видно остальные методы нам не нужны, и мы их не используем. Здесь явное нарушение ISP,
    т.к. интерфейс навязывает использование других методов

    5. Dependency inversion principle - принцип инверсии зависимостей.
    Высокоуровневые модули не должны зависеть от низкоуровневых.
    Абстракции не должны зависеть от деталей. Но детали зависят от абстракций.
    Пример: у нас есть класс Repository, отвечающий за получение данных из разных источников.
    Проблема в том, что объекты жестко заданы в конструкторе. И мы не имеем возможности
    поменять реализацию AppDataBase на FakeDataBase для тестов.
    spoiler
    public class Repository{
    	private final NetworkManager networkManager;
    	private final AppDataBase appDataBase;
    	public Repository(){
    		this.networkManager = new NetworkManager();
    		this.AppDataBase = new AppDataBase();
    	}
    }


    Поэтому нам следует как-то развязать жесткую связь, выделив интерфейсы.
    spoiler
    public interface RemoteDataSource{}
    public interface LocalDataSource{}
    public class NetworkManager implements RemoteDataSource{}
    public class AppDataBase implements LocalDataSource{}
    
    public class Repository{
    	private final RemoteDataSource remoteDataSource;
    	private final LocalDataSource localDataSource;
    	public Repository(RemoteDataSource remoteDataSource, LocalDataSource localDataSource){
    		this.remoteDataSource = remoteDataSource;
    		this.localDataSource = localDataSource;
    	}
    }


    А теперь мы можем тестовую реализацию источников:
    public class FakeNetworkManager implements RemoteDataSource{}
    public class FakeAppDataBase implements LocalDataSource{}

    и вызов:
    Repository repository = new Repository(new FakeNetworkManager(), new FakeAppDataBase());


    У Мартина Фаулера есть хорошая книга Рефакторинг: Улучшение существующего кода и Р.Мартина Чистый код
    Ответ написан
    2 комментария
  • Как построить логику архитектуры MVVM в моем приложении?

    @terminator-light
    View может вызывать методы VM.
    Но обратно нет.
    Например, во View, т.е. в MainActivity у вас есть такой код:
    editText.addTextChangedListener(new OnTextChangedListener() {
                @Override
                public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                  //вызываете метод VM
                    viewModel.loadWeatherData();
                }
    });
    //подписываетесь на изменения данных во VM
    viewModel.getWeatherData().observe(this, data -> {
              //данные изменились, можно показать тост или сделать что-то другое... Можно показать измененные данные, в конце концов
    });

    а в VM:
    public class WeatherViewModel extends ViewModel {
          private final MutableLiveData<WeatherData> weatherData = new MutableLiveData<>();
          public LiveData<WeatherData> getWeatherData() {
            return checkout;
          }
          public void loadWeatherData(){
                //из своего API получаете данные и изменяете weatherData
                disposable.add(repository.fetchWeatherData() 
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread());
                    .subscribe((data, throwable) -> {
                       //при вызове setValue вызывается коллбек у подписчиков на weatherData
                        this.weatherData.setValue(data);
                    }
            ));
          }
    }
    Ответ написан
    Комментировать
  • Как разместить LIstView вместе с другими элементами, чтобы всё влезало?

    @terminator-light
    в ConstraintLayout вместо match_parent следует использовать 0dp - это значит, что будет использоваться все допустимое пространство, заданное ограничениями
    Ответ написан
  • Что в Андроид в основном используются для фоновой работы и работы с потоками?

    @terminator-light
    Для работы с потоками обычно выбирают между RxJava и Coroutines, остальное можно забыть. А если имеется в виду, фоновая работа в Android, то это Service
    Ответ написан
  • Как реализовать добавления товаров в корзину в Android приложении?

    @terminator-light
    SharedPreferences обычно используется для хранения настроек пользователя, а для таких вещей естественно используют SQLite
    Ответ написан
    Комментировать
  • Нужна помощь, как обратится к предыдущему фрагменты на удаление?

    @terminator-light
    тут есть 2 подхода:
    1) простой:
    используя жизненный цикл фрагментов, можно в коллбеке onResume просто перезагрузить список
    минус: перезагрузится весь список

    2) правильный

    создать интерфейс, реализуемый активити
    interface OnRemoveItemListener{
       void removeItem(int pos);
    }
    
    public class ListFragment extends Fragment {
    	...
        public void itemRemoved(int pos){
        	adapter.getData().remove(pos);
        	adapter.notifyItemRemoved(pos);
        	//это все можно делать внутри адаптера, а здесь просто вызвать его метод
        }
    
    }
    
    public class DetailFragment extends Fragment {
    	private OnRemoveItemListener listener;
    	//позиция элемента в адаптере RecyclerView
    	private int position;
    
    	public static void newInstance(int pos){
    		Bundle bundle = new Bundle();
    		bundle.putInt("pos", pos);
    		DetailFragment f = new DetailFragment();
    		f.setArguments(bundle);
    		return f;
    	}
    
    	@Override
        public void onAttach(Context context) {
            super.onAttach(context);
            try {
                listener = (OnRemoveItemListener) context;
            } catch (ClassCastException e) {
                throw new ClassCastException(context.toString() + " must implement OnRemoveItemListener");
            }
        }
    
        @Override
        public void onDetach() {
            super.onDetach();
            listener = null;
        }
    
        public void onViewCreated(...){
        	//получаете pos из getArguments() и сохраняете в поле position
        }
    
    //метод удаления на сервере
        public void removeItemFromApi(){
        	...
        	listener.removeItem(position);
        	...
        }
    
    }
    
    public class MainActivity extends AppCompatActivity implements OnRemoveItemListener{
    	void removeItem(int pos){
    		ListFragment f = (ListFragment)getSupportFragmentManager().findFragmentById(R.id.detailFragment);
    		f.itemRemoved(pos);
    	}
    }
    Ответ написан
    3 комментария
  • Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $ почему возникает ошибка?

    @terminator-light
    Expected BEGIN_ARRAY but was BEGIN_OBJECT

    говорит о том, что ожидался массив, но был получен объект.
    Иначе говоря ожидался List<T>, но был получен объект List.
    Поэтому параметризуйте List:
    private List<CityValue> city;
    public List<CityValue> getCity() {
        return city;
    }
    public void setCity(List<CityValue> city) {
        this.city = city;
    }
    Ответ написан
    Комментировать
  • Как сделать закругление кнопок?

    @terminator-light
    <style name="AppTheme" parent="YourTheme">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
            <item name="materialButtonStyle">@style/systemButtonStyle</item> <!-- если используете MaterialComponents -->
            <item name="buttonStyle">@style/systemButtonStyle</item> <!-- если используете AppCompat  -->
     </style>
    
    <style name="systemButtonStyle" parent="Widget.MaterialComponents.Button">
          <item name="android:background">@drawable/roundedbutton</item>
    </style>

    drawable/roundedbutton.xml
    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android" 
    android:shape="rectangle">
        <solid android:color="#ff0000" />
        <corners android:radius="8dp"/>
    </shape>
    Ответ написан
    5 комментариев
  • Нормально ли изучать android разработку по startandroid?

    @terminator-light
    Советую такие каналы по Android-разработке как:
    Mobile Developer
    Skill Branch
    А если знаете английский:
    Reso Coder
    CodingWithMitch
    Ответ написан
    Комментировать
  • Хочу разобрать некоторые моменты UI в приложении?

    @terminator-light
    1) Виджеты располагаются иерархично. Родительскому, который содержит дочерние элементы, устанавливается слушатель, если действие одно.
    По клику на нижний элемент играет анимация отплывающих волн, словно бросили камень в воду, это называется ripple effect, или же своя кастомная. Чтобы такое сделать создается/используется соответствующий/существующий drawable и устанавливается в качестве background.
    android:background="?attr/selectableItemBackground"

    2) Это может быть либо RecyclerView, либо ViewPager или кастомная View
    3) Такое поведение достигается путем совмещения CoordinatorLayout и CollapsingToolbarLayout Android CollapsingToolbarLayout Example
    Ответ написан
    Комментировать
  • Как при нажатии enter на одном EditText перейти на другой EditText?

    @terminator-light
    уберите лисенер с EditText, просто добавьте к разметке
    android:maxLines="1"
    android:inputType="text"
    android:imeOptions="actionNext"

    How to move your focus through EditText on Android
    Ответ написан