Страницы

Поиск по вопросам

Показаны сообщения с ярлыком android-navigation-drawer. Показать все сообщения
Показаны сообщения с ярлыком android-navigation-drawer. Показать все сообщения

пятница, 13 марта 2020 г.

Android: правильное использование фрагментов и Navigation Drawer

#android #android_fragment #dialogfragment #android_navigation_drawer


Я использую в приложении шаблон Navigation Drawer Activity и у меня возникли несколько
вопросов по поводу правильного использования его и фрагментов в целом.


Т.к. все страницы (назову фрагменты, открываемые из бокового меню так) приложения
- фрагменты в одной активити, то я лишаюсь возможности использовать Intent для переключения
активиностей?
Как реализовать открытие фрагмента (какой-то страницы) по какому-то действию из другого
фрагмента? Сделать интерфейс, напримерIFragmentAction, со всеми событиями, которые
могут послать фрагменты, реализовать его у активити и вызывать эти методы из фрагментов?
А в них уже делать транзакции? Это правильно?
Аналогично, если нужно по действию во фрагменте показать фрагмент, не явлюящийся
"страницей" - надо делать аналогично, через активити? Или можно сделать транзакцию
прямо из фрагмента (в настоящий момент сделал так, но кажется это немного неправильно)
Если нужен режим для планшета, где некоторые фрагменты не открываются поверх старых,
а расположены рядом - надо создать новый layouyt еще одну активити, реализующую интерфейс,
но с другим поведением? Или это не самый правильный подход?
Как правильно вернуть результат из DialogFragment? Я реализовывал интерфейс у фрагмента,
который вызывал этот диалог, через который диалог устанавливал нужные значения. Кажется,
не лучший подход.
Столкнулся с такой проблемой: 
Если после нескольких смен фрагментов пользователь переходит через боковое меню на
другую страницу - он видит несколько наложившихся активностей. Если просто сделать
белый фон - задняя активность все равно останется кликабельной. 


Как правильно сделать переходы между активностями, чтобы не возникало таких проблем
и чтобы это было правильно?
    


Ответы

Ответ 1



Отвечу на всю историю примерным кодом. Пусть Ваш NavigationDrawer содержит кастомный адаптер для списка. При нажатии в списке на элемент вы к примеру получите йд нужного фрагмента, пусть это будет int, Но лучше строка. Получаем TAG Фрагмента. private String getTagById(int tagId) { String tag = null; if (Frag1.TAG_ID == tagId) { tag = Frag1.TAG; } else if (Frag2.TAG_ID == tagId) { tag = Frag2.TAG; } return tag; } Дальше в роль вступает участок кода для работы с фрагментами. В нем мы узнаем совпадает ли активный фрагмент с выбранным, если не совпадает мы сделаем detach активного, проверим открывали ли ранее выбранный и выполним replace или attach private void showFragment(String tag) { String oldTag = mSelectedTag; mSelectedTag = tag; final FragmentManager fm = getSupportFragmentManager(); final FragmentTransaction ft = fm.beginTransaction(); final Fragment oldFragment = fm.findFragmentByTag(oldTag); final Fragment fragment = fm.findFragmentByTag(tag); if (oldFragment != null && !tag.equals(oldTag)) { ft.detach(oldFragment); } if (fragment == null) { ft.replace(R.id.container, getContentFragment(tag), tag); } else { if (fragment.isDetached()) { ft.attach(fragment); } } ft.commit(); } Этот кусок кода отдает нужный фрагмент по тагу. private Fragment getContentFragment(String tag) { Fragment fragment = null; if (Frag1.TAG.equals(tag)) { fragment = new Frag1(); } else if (Frag2.TAG.equals(tag)) { fragment = new Frag2(); } return fragment; } А вот так все запускаем: @SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) { showFragment(getTagById(item.getItemId())); DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; }

суббота, 11 января 2020 г.

Изменить ширину NavigationDrawer

#java #android #android_navigation_drawer


Есть activity , в котором я использую кастомный FullDrawerLayout с Gravity=start
, внутри этого activity есть ViewPager внутри которого фрагменты, и вот в одном из
этих фрагментов я хочу добавить NavigationDrawer с Gravity=end и использую уже android.support.v4.widget.DrawerLayout
и вот в нем я хочу изменить ширину, делаю это так:

    drawerRecycler = (RecyclerView) rootView.findViewById(R.id.recyclerView);
    RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
    drawerRecycler.setLayoutManager(mLayoutManager);
    drawerRecycler.setItemAnimator(new DefaultItemAnimator());

    RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) drawerLayout.getLayoutParams();
    params.width = 100;
    drawerLayout.setLayoutParams(params);


но падает с вот такой ошибкой:


  java.lang.IllegalArgumentException: DrawerLayout must be measured with
  MeasureSpec.EXACTLY.


но эта ошибка ведет в NavigationDrawer который в активити, не могу понять при чем
тут он. Вот код NavigationDrawer который я использую в активити:

public class FullDrawerLayout extends DrawerLayout {

    private static final int MIN_DRAWER_MARGIN = 0; // dp

    public FullDrawerLayout(Context context) {
        super(context);
    }

    public FullDrawerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FullDrawerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalArgumentException(
                    "DrawerLayout must be measured with MeasureSpec.EXACTLY.");
        }

        setMeasuredDimension(widthSize, heightSize);

        // Gravity value for each drawer we've seen. Only one of each permitted.
        int foundDrawers = 0;
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);

            if (child.getVisibility() == GONE) {
                continue;
            }

            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            if (isContentView(child)) {
                // Content views get measured at exactly the layout's size.
                final int contentWidthSpec = MeasureSpec.makeMeasureSpec(
                        widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
                final int contentHeightSpec = MeasureSpec.makeMeasureSpec(
                        heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
                child.measure(contentWidthSpec, contentHeightSpec);
            } else if (isDrawerView(child)) {
                final int childGravity =
                        getDrawerViewGravity(child) & Gravity.HORIZONTAL_GRAVITY_MASK;
                if ((foundDrawers & childGravity) != 0) {
                    throw new IllegalStateException("Child drawer has absolute gravity " +
                            gravityToString(childGravity) + " but this already has a " +
                            "drawer view along that edge");
                }
                final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec,
                        MIN_DRAWER_MARGIN + lp.leftMargin + lp.rightMargin,
                        lp.width);
                final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec,
                        lp.topMargin + lp.bottomMargin,
                        lp.height);
                child.measure(drawerWidthSpec, drawerHeightSpec);
            } else {
                throw new IllegalStateException("Child " + child + " at index " + i +
                        " does not have a valid layout_gravity - must be Gravity.LEFT, " +
                        "Gravity.RIGHT or Gravity.NO_GRAVITY");
            }
        }
    }

    boolean isContentView(View child) {
        return ((DrawerLayout.LayoutParams) child.getLayoutParams()).gravity == Gravity.NO_GRAVITY;
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    boolean isDrawerView(View child) {
        final int gravity = ((DrawerLayout.LayoutParams) child.getLayoutParams()).gravity;
        final int absGravity = Gravity.getAbsoluteGravity(gravity,
                child.getLayoutDirection());
        return (absGravity & (Gravity.LEFT | Gravity.RIGHT)) != 0;
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    int getDrawerViewGravity(View drawerView) {
        final int gravity = ((DrawerLayout.LayoutParams) drawerView.getLayoutParams()).gravity;
        return Gravity.getAbsoluteGravity(gravity, drawerView.getLayoutDirection());
    }

    static String gravityToString(int gravity) {
        if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
            return "LEFT";
        }
        if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
            return "RIGHT";
        }
        return Integer.toHexString(gravity);
    }

}


Здесь падает в строчке 

сhild.measure(contentWidthSpec, contentHeightSpec);

    


Ответы

Ответ 1



Решил проблему, сделал это через верстку, просто установив ширину для RecyclerView и установил ему привязку к правому краю родителя:

суббота, 21 декабря 2019 г.

Как в NavigationView в header установить текст?

#java #android #android_layout #android_navigation_drawer #android_navigation_view


У меня есть вот такой NavigationView 



Вот такой код:











и вот такой header:













Мне нужно в этот header в поле для имени user поставить его имя, а в поле для email
поставить соответственно email

Я понимаю, что все стандартно и нужно найти по id view с которым хочешь работать
и установить в него TextView. 

Но когда я нахожу view и ставлю text, то вылетает ошибка, что такого view еще нет.
Это логично т.к. NavigationView еще не открыт...

Так вот вопрос. Как отследить что NavigationView уже открыт и только после этого
устанавливать text в view?

Если я в onCreate() нахожу и устанавливаю текст

TextView tvName = (TextView) findViewById(R.id.tvName);
    TextView textEmail = (TextView) findViewById(R.id.textEmail);

    tvName.setText("test");
    textEmail.setText("test");


то получаю такую ошибку в строке tvName.setText("test");


  FATAL EXCEPTION: main
                                                                                
   Process: com.example.android.camera2basic, PID: 32152
                                                                                
   java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.android.camera2basic/com.example.android.camera2basic.activities.MainActivity}:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)'
on a null object reference


Если применять такой подход, то ошибки нет, но текст остается таким как по умолчанию

NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);
    RelativeLayout headerView = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.nav_header_mainsecond,
null);
    TextView tvName = (TextView) headerView.findViewById(R.id.tvName);
    TextView textEmail = (TextView) headerView.findViewById(R.id.textEmail);
    tvName.setText("name");
    textEmail.setText("email");

    


Ответы

Ответ 1



Вам надо: Получить NavigationView NavigationView nv = ...; Получить разметку его Header View header = nv.getHeaderView(0); Найти нужные поля и с ними что-то сделать TextView tv = (TextView)header.findViewById(...);

пятница, 20 декабря 2019 г.

Как сделать иконки в Navigation Drawer цветными?

#android #android_navigation_drawer


Не понимаю почему, но иконки отображаются серыми, хотя сами они все цветные.



Вот код Activity_main.xml, может быть пригодится.












    


Ответы

Ответ 1



Попробуйте оттенок убрать navigationView.setItemIconTintList(null);

среда, 11 декабря 2019 г.

Android/Java. Как задать (увеличить) отступ иконки Navigation Drawer'a от края?

#java #android #android_toolbar #android_navigation_drawer #icon


Как задать (увеличить) отступ иконки Navigation Drawer'a от края?


    


Ответы

Ответ 1



Я думаю если человек использует Navigation Drawer, то там используется "android.support.v7.widget.Toolbar" Тогда если вы хотите сделать отступ, используйте такой пример: где android:layout_marginLeft="15dp" и есть отступ от левого края на 15dp.

вторник, 9 июля 2019 г.

В чём отличие Sliding Menu от Navigation Drawer?

Насколько я понимаю, и то, и другое - выплывающее, как правило, с левой стороны боковое меню. В чём тогда отличие этих терминов?


Ответ

Sliding Menu - сдвигает основной контент, находится на одной позиции по Z оси Navigation Drawer - перекрывает основной контент, находится выше по Z оси

понедельник, 17 июня 2019 г.

Android Studio. Не отображает Navigation Drawer при нажатии на гамбургер

В Debugger при нажатие на гамбургер item равно ActionMenuItem@4876, как выправить код чтоб все заработало?
private void setToolBar() {
Toolbar tb = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(tb);
ActionBar ab = getSupportActionBar(); ab.setHomeAsUpIndicator(R.drawable.ic_menu_default); ab.setDisplayHomeAsUpEnabled(true); }
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.forecast_filter: showFiter(); break; case R.id.home: { dLayout.openDrawer(GravityCompat.START); return true;} } return true; }


Ответ

Вы используете неправильный ID. Вам надо вместо
R.id.home
использовать системный ID из пакета android, т.е.
android.R.id.home

суббота, 1 июня 2019 г.

Android: правильное использование фрагментов и Navigation Drawer

Я использую в приложении шаблон Navigation Drawer Activity и у меня возникли несколько вопросов по поводу правильного использования его и фрагментов в целом.
Т.к. все страницы (назову фрагменты, открываемые из бокового меню так) приложения - фрагменты в одной активити, то я лишаюсь возможности использовать Intent для переключения активиностей? Как реализовать открытие фрагмента (какой-то страницы) по какому-то действию из другого фрагмента? Сделать интерфейс, напримерIFragmentAction, со всеми событиями, которые могут послать фрагменты, реализовать его у активити и вызывать эти методы из фрагментов? А в них уже делать транзакции? Это правильно? Аналогично, если нужно по действию во фрагменте показать фрагмент, не явлюящийся "страницей" - надо делать аналогично, через активити? Или можно сделать транзакцию прямо из фрагмента (в настоящий момент сделал так, но кажется это немного неправильно) Если нужен режим для планшета, где некоторые фрагменты не открываются поверх старых, а расположены рядом - надо создать новый layouyt еще одну активити, реализующую интерфейс, но с другим поведением? Или это не самый правильный подход? Как правильно вернуть результат из DialogFragment? Я реализовывал интерфейс у фрагмента, который вызывал этот диалог, через который диалог устанавливал нужные значения. Кажется, не лучший подход. Столкнулся с такой проблемой: Если после нескольких смен фрагментов пользователь переходит через боковое меню на другую страницу - он видит несколько наложившихся активностей. Если просто сделать белый фон - задняя активность все равно останется кликабельной.
Как правильно сделать переходы между активностями, чтобы не возникало таких проблем и чтобы это было правильно?


Ответ

Отвечу на всю историю примерным кодом.
Пусть Ваш NavigationDrawer содержит кастомный адаптер для списка. При нажатии в списке на элемент вы к примеру получите йд нужного фрагмента, пусть это будет int, Но лучше строка. Получаем TAG Фрагмента.
private String getTagById(int tagId) { String tag = null; if (Frag1.TAG_ID == tagId) { tag = Frag1.TAG; } else if (Frag2.TAG_ID == tagId) { tag = Frag2.TAG; } return tag; }
Дальше в роль вступает участок кода для работы с фрагментами. В нем мы узнаем совпадает ли активный фрагмент с выбранным, если не совпадает мы сделаем detach активного, проверим открывали ли ранее выбранный и выполним replace или attach
private void showFragment(String tag) { String oldTag = mSelectedTag; mSelectedTag = tag; final FragmentManager fm = getSupportFragmentManager(); final FragmentTransaction ft = fm.beginTransaction(); final Fragment oldFragment = fm.findFragmentByTag(oldTag); final Fragment fragment = fm.findFragmentByTag(tag);
if (oldFragment != null && !tag.equals(oldTag)) { ft.detach(oldFragment); }
if (fragment == null) { ft.replace(R.id.container, getContentFragment(tag), tag); } else { if (fragment.isDetached()) { ft.attach(fragment); } } ft.commit(); }
Этот кусок кода отдает нужный фрагмент по тагу.
private Fragment getContentFragment(String tag) { Fragment fragment = null; if (Frag1.TAG.equals(tag)) { fragment = new Frag1(); } else if (Frag2.TAG.equals(tag)) { fragment = new Frag2(); } return fragment; }
А вот так все запускаем:
@SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) { showFragment(getTagById(item.getItemId())); DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; }

понедельник, 12 ноября 2018 г.

Как в NavigationView в header установить текст?

У меня есть вот такой NavigationView

Вот такой код:




и вот такой header





Мне нужно в этот header в поле для имени user поставить его имя, а в поле для email поставить соответственно email
Я понимаю, что все стандартно и нужно найти по id view с которым хочешь работать и установить в него TextView.
Но когда я нахожу view и ставлю text, то вылетает ошибка, что такого view еще нет. Это логично т.к. NavigationView еще не открыт...
Так вот вопрос. Как отследить что NavigationView уже открыт и только после этого устанавливать text в view?
Если я в onCreate() нахожу и устанавливаю текст
TextView tvName = (TextView) findViewById(R.id.tvName); TextView textEmail = (TextView) findViewById(R.id.textEmail);
tvName.setText("test"); textEmail.setText("test");
то получаю такую ошибку в строке tvName.setText("test");
FATAL EXCEPTION: main Process: com.example.android.camera2basic, PID: 32152 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.android.camera2basic/com.example.android.camera2basic.activities.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
Если применять такой подход, то ошибки нет, но текст остается таким как по умолчанию
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); navigationView.setNavigationItemSelectedListener(this); RelativeLayout headerView = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.nav_header_mainsecond, null); TextView tvName = (TextView) headerView.findViewById(R.id.tvName); TextView textEmail = (TextView) headerView.findViewById(R.id.textEmail); tvName.setText("name"); textEmail.setText("email");


Ответ

Вам надо:
Получить NavigationView
NavigationView nv = ...;
Получить разметку его Header
View header = nv.getHeaderView(0);
Найти нужные поля и с ними что-то сделать
TextView tv = (TextView)header.findViewById(...);

четверг, 8 ноября 2018 г.

Как сделать иконки в Navigation Drawer цветными?

Не понимаю почему, но иконки отображаются серыми, хотя сами они все цветные.

Вот код Activity_main.xml, может быть пригодится.





Ответ

Попробуйте оттенок убрать
navigationView.setItemIconTintList(null);