Подтвердить что ты не робот

Оптимизация скорости запуска и активности

Я использую Google DrawerLayout.

Когда элемент получает щелчок, ящик плавно закрывается и запускается Activity. Включение этих действий в Fragment не является вариантом. Из-за этого запуск активности, а затем закрытие ящика также не является вариантом. Закрытие ящика и запуск активности в то же время заставят закрытие анимации заикаться.

Учитывая, что я хочу сначала сгладить его, а затем запустить действие, у меня возникла проблема с задержкой между тем, когда пользователь нажимает на элемент ящика, и когда они видят активность, к которой они хотели перейти.

Это то, что выглядит слушатель кликов для каждого элемента.

final View.OnClickListener mainItemClickListener = new View.OnClickListener() {
    @Override
    public void onClick(final View v) {
        mViewToLaunch = v;
        mDrawerLayout.closeDrawers();
    }
};

Моя активность также является DrawerListener, ее метод onDrawerClosed выглядит следующим образом:

@Override
public synchronized void onDrawerClosed(final View view) {
    if (mViewToLaunch != null) {
        onDrawerItemSelection(mViewToLaunch);
        mViewToLaunch = null;
    }
}

onDrawerItemSelection просто запускает одно из пяти действий.

Я ничего не делаю на onPause DrawerActivity.

Я занимаюсь этим, и он принимает в среднем от 500-650 мс с момента вызова onClick, до момента окончания концаDrawerClosed.

Наблюдается заметное отставание, когда ящик закрывается, до запуска соответствующего действия.

Я понимаю, что происходит несколько вещей:

  • Заключительная анимация имеет место, что составляет пару миллисекунд прямо (скажем 300).

  • Затем, вероятно, есть некоторое задержка между визуальным закрытием ящика и его прослушивателем. Я пытаюсь выяснить, сколько из этого происходит посмотрев DrawerLayout источник, но пока не понял.

  • Затем потребуется время, затрачиваемое на запущенную активность для выполнения своих жизненных циклов запуска, вплоть до onResume. Я пока ничего не сделал, но я оцениваю около 200-300 м.

Это похоже на проблему, когда спуск по неправильному пути будет довольно дорогостоящим, поэтому я хочу убедиться, что полностью его понимаю.

Одним из решений является просто пропустить закрытие анимации, но я надеялся сохранить его.

Как я могу максимально уменьшить время перехода?

4b9b3361

Ответ 1

В соответствии с docs,

Избегайте выполнения дорогостоящих операций, таких как макет во время анимации, поскольку это может вызвать заикание; попытайтесь выполнить дорогостоящие операции в состоянии STATE_IDLE.

Вместо использования Handler и жесткого кодирования временной задержки вы можете переопределить метод onDrawerStateChanged ActionBarDrawerToggle (который реализует DrawerLayout.DrawerListener), чтобы вы могли выполнять дорогостоящие операции, когда ящик полностью закрыт.

Внутри MainActivity,

private class SmoothActionBarDrawerToggle extends ActionBarDrawerToggle {

    private Runnable runnable;

    public SmoothActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, Toolbar toolbar, int openDrawerContentDescRes, int closeDrawerContentDescRes) {
        super(activity, drawerLayout, toolbar, openDrawerContentDescRes, closeDrawerContentDescRes);
    }

    @Override
    public void onDrawerOpened(View drawerView) {
        super.onDrawerOpened(drawerView);
        invalidateOptionsMenu();
    }
    @Override
    public void onDrawerClosed(View view) {
        super.onDrawerClosed(view);
        invalidateOptionsMenu();
    }
    @Override
    public void onDrawerStateChanged(int newState) {
        super.onDrawerStateChanged(newState);
        if (runnable != null && newState == DrawerLayout.STATE_IDLE) {
            runnable.run();
            runnable = null;
        }
    }

    public void runWhenIdle(Runnable runnable) {
        this.runnable = runnable;
    }
}

Установите DrawerListener в onCreate:

mDrawerToggle = new SmoothActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.open, R.string.close);
mDrawerLayout.setDrawerListener(mDrawerToggle);

Наконец,

private void selectItem(int position) {
    switch (position) {
        case DRAWER_ITEM_SETTINGS: {
            mDrawerToggle.runWhenIdle(new Runnable() {
                @Override
                public void run() {
                    Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
                    startActivity(intent);
                }
            });
            mDrawerLayout.closeDrawers();
            break;
        }
        case DRAWER_ITEM_HELP: {
            mDrawerToggle.runWhenIdle(new Runnable() {
                @Override
                public void run() {
                    Intent intent = new Intent(MainActivity.this, HelpActivity.class);
                    startActivity(intent);
                }
            });
            mDrawerLayout.closeDrawers();
            break;
        }
    }
}

Ответ 2

Я столкнулся с той же проблемой с DrawerLayout.

У меня есть исследование для этого, а затем найдите для него одно приятное решение.

То, что я делаю, это.....

Если вы обратитесь к приложению Android Sample для DrawerLayout, тогда проверьте код для selectItem (position);

В этой функции на основе фрагмента выбора позиции вызывается. Я изменил его с помощью ниже кода в соответствии с моей потребностью и отлично работает без анимации, заикающейся.

private void selectItem(final int position) {
    //Toast.makeText(getApplicationContext(), "Clicked", Toast.LENGTH_SHORT).show();
    mDrawerLayout.closeDrawer(drawerMain);
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            Fragment fragment = new TimelineFragment(UserTimeLineActivity.this);
            Bundle args = new Bundle();
            args.putInt(TimelineFragment.ARG_PLANET_NUMBER, position);
            fragment.setArguments(args);

            FragmentManager fragmentManager = getSupportFragmentManager();
            fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();

            // update selected item and title, then close the drawer
            mCategoryDrawerList.setItemChecked(position, true);

            setTitle("TimeLine: " + mCategolyTitles[position]);
        }
    }, 200);


    // update the main content by replacing fragments


}

Здесь я закрываю DrawerLayout. который занимает около 250 миллисекунд. а затем мой обработчик вызовет фрагмент. Который работает гладко и согласно требованию.

Надеюсь, что это также поможет вам.

Наслаждайтесь кодированием...:)

Ответ 3

Итак, я, кажется, решил проблему с разумным решением.

Наибольшим источником воспринимаемой задержки была задержка между тем, когда ящик визуально закрыт, и когда был вызван onDrawerClosed. Я решил это, отправив Runnable в частный Handler, который запускает запланированную активность с определенной задержкой. Эта задержка выбирается так, чтобы соответствовать закрытию ящика.

Я попытался запустить запуск onDrawerSlide после 80% -ного прогресса, но это имеет две проблемы. Во-первых, он заикался. Во-вторых, если вы увеличили процент до 90% или 95%, вероятность того, что его вообще не вызвали из-за характера анимации, увеличилась - и вам тогда пришлось вернуться к onDrawerClosed, что побеждает цель.

Это решение имеет возможность заикаться, особенно на старых телефонах, но вероятность может быть уменьшена до 0 просто путем увеличения задержки достаточно высокой. Я думал, что 250 мс был разумным балансом между заиканием и латентностью.

Соответствующие части кода выглядят следующим образом:

public class DrawerActivity extends SherlockFragmentActivity {
    private final Handler mDrawerHandler = new Handler();

    private void scheduleLaunchAndCloseDrawer(final View v) {
        // Clears any previously posted runnables, for double clicks
        mDrawerHandler.removeCallbacksAndMessages(null); 

        mDrawerHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                onDrawerItemSelection(v);
            }
        }, 250);
        // The millisecond delay is arbitrary and was arrived at through trial and error

        mDrawerLayout.closeDrawer();
    }
}

Ответ 4

Google IOsched 2015 работает очень плавно (кроме настроек), причина в том, как они реализовали ящик и как они запускают материал.

Первый из них использует обработчик для запуска с задержкой:

        // launch the target Activity after a short delay, to allow the close animation to play
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                goToNavDrawerItem(itemId);
            }
        }, NAVDRAWER_LAUNCH_DELAY);

с задержкой:

private static final int NAVDRAWER_LAUNCH_DELAY = 250;

Еще одна вещь, которую они делают, - удалить анимации из действий, которые запускаются с помощью следующего кода внутри действий onCreate():

overridePendingTransition(0, 0);

Для просмотра источника перейдите в git.

Ответ 5

Я использую подход, как показано ниже. Работает плавно.

public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener {

    private DrawerLayout drawerLayout;
    private MenuItem menuItemWaiting;

    /* other stuff here ... */

    private void setupDrawerLayout() {

        /* other stuff here ... */

        drawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);
                if(menuItemWaiting != null) {
                    onNavigationItemSelected(menuItemWaiting);
                }
            }
        });

    }

    @Override
    public boolean onNavigationItemSelected(MenuItem menuItem) {

        menuItemWaiting = null;
        if(drawerLayout.isDrawerOpen(GravityCompat.START)) {
            menuItemWaiting = menuItem;
            drawerLayout.closeDrawers();
            return false;
        };

        switch(menuItem.getItemId()) {
            case R.id.drawer_action:
                startActivity(new Intent(this, SecondActivity.class));

            /* other stuff here ... */

        }
        return true;
    }
}

То же самое с ActionBarDrawerToggle:

drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close){
    @Override
    public void onDrawerClosed(View drawerView) {
        super.onDrawerClosed(drawerView);
        if(menuItemWaiting != null) {
            onNavigationItemSelected(menuItemWaiting);
        }
    }
};
drawerLayout.setDrawerListener(drawerToggle);

Ответ 6

Лучшим подходом было бы использовать метод onDrawerSlide (View, float) и запустить Activity, как только slideOffset будет равен 0. См. ниже

public void onDrawerSlide(View drawerView, float slideOffset) {
    if (slideOffset <= 0 && mPendingDrawerIntent != null) {
        startActivity(mPendingDrawerIntent);
        mPendingDrawerIntent = null;
    }
}

Просто установите параметр mPendingDrawerIntent в методе Drawer ListView.OnItemClickListener onItemClick.

Ответ 7

Вот как я это делаю, не задавая какой-либо задержки,

public class MainActivity extends AppCompatActivity {

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

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        LazyNavigationItemSelectedListener lazyNavigationItemSelectedListener =
            new LazyNavigationItemSelectedListener(this, drawer, "drawer_open", "drawer_close");
        drawer.addDrawerListener(navigationItemSelectedListener);
        lazyNavigationItemSelectedListener.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(lazyNvigationItemSelectedListener);
    }
  .
  .
  .

}

а LazyNavigationItemSelectedListener может быть внутренним классом MainActivity.

private class LazyNavigationItemSelectedListener extends ActionBarDrawerToggle
        implements NavigationView.OnNavigationItemSelectedListener {
    private int selectedMenuItemID;
    DrawerLayout drawer;

    private LazyNavigationItemSelectedListener(Activity activity, DrawerLayout drawerLayout, 
                                              @StringRes int openDrawerContentDescRes, 
                                              @StringRes int closeDrawerContentDescRes) {
        super(activity, drawerLayout, openDrawerContentDescRes, closeDrawerContentDescRes);
        this.drawer = drawerLayout;
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        }
        selectedMenuItemID = item.getItemId();

        if (!(drawer.isDrawerOpen(GravityCompat.START))) {//only respond to call if drawer is closed.

            switch (selectedMenuItem) {

                case R.id.menu_item_id:
                    Intent intent1 = new Intent() //build your intent
                    startActivity(intent1);
                    break;
            }
        }
        return true;
    }

    @Override
    public void onDrawerClosed(View drawerView) {
        if (selectedMenuItemID > 0) {
            if (drawerView instanceof NavigationView) {
                NavigationView navigationView = (NavigationView) drawerView;
                //perform click on navigation item.
                navigationView.getMenu().performIdentifierAction(selectedMenuItemID, 0);
                selectedMenuItemID = -1;
            }
        }
    }
}

Ответ 8

Этот ответ предназначен для парней, которые используют RxJava и RxBinding. Идея заключается в том, чтобы предотвратить запуск активности до закрытия ящика. NavigationView используется для отображения меню.

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{

  private DrawerLayout drawer;

  private CompositeDisposable compositeDisposable;

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

    // setup views and listeners (NavigationView.OnNavigationItemSelectedListener)

    compositeDisposable = new CompositeDisposable();
    compositeDisposable.add(observeDrawerClose());

  }

  // uncomment if second activitiy comes back to this one again
  /*
  @Override
  protected void onPause() {
      super.onPause();
      compositeDisposable.clear();
  }

  @Override
  protected void onResume() {
     super.onResume();
     compositeDisposable.add(observeDrawerClose());
  }*/

  @Override
  protected void onDestroy() {
    super.onDestroy();
    compositeDisposable.clear();
  }

  @Override
  public boolean onNavigationItemSelected(MenuItem item) {
    // Handle navigation view item clicks here.
    int id = item.getItemId();

    navSubject.onNext(id);

    drawer.closeDrawer(GravityCompat.START);
    return true;
  }

  private Disposable observeDrawerClose() {
    return RxDrawerLayout.drawerOpen(drawer, GravityCompat.START)
        .skipInitialValue() // this is important otherwise caused to zip with previous drawer event
        .filter(open -> !open)
        .zipWith(navSubject, new BiFunction<Boolean, Integer, Integer>() {
          @Override
          public Integer apply(Boolean aBoolean, Integer u) throws Exception {
            return u;
          }
        }).subscribe(id -> {
          if (id == R.id.nav_home) {
            // Handle the home action
          } else {

          }
        });
  }
}