Детский в FrameLayout не рисуется - PullRequest
0 голосов
/ 04 февраля 2012

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

В любом случае, у меня есть проблема, когда мне нужно надутьдобавить представления к FrameLayout при загрузке Fragment.Дочерние представления получены из информации в базе данных SQLite.Чтобы правильно расположить дочерние виды (в зависимости от ориентации), мне нужно учитывать ширину и высоту FrameLayout.Поэтому я не могу выполнить эту работу в onCreateView() в моем Fragment.

. FrameLayout на самом деле DragArea (хотя я все равно буду ссылаться на этот объект как FrameLayout,для простоты) пользовательский класс, расширяющий FrameLayout.Чтобы получить обратный вызов к Fragment, когда ширина / высота была вычислена, я переопределяю onSizeChanged в FrameLayout, который вызывает Fragment.

И вот странная вещь.Если я в методе обратного вызова в моем Fragment создаю представления и вызываю FrameLayout.invalidate, кажется, ничего не происходит.Инспекция говорит, что представления были добавлены к FrameLayout, но они не видны.Если я открою просмотрщик иерархии и нажму «Загрузить иерархию представлений», отобразятся представления.Так что, похоже, здесь есть побочный эффект, который притягивает взгляды.Я попытался вызвать FrameLayout.requestLayout(), а также убедиться, что он работает в потоке пользовательского интерфейса, выполнив логику в getActivity().runOnUiThread(), но это не имеет значения.

НО! Если вместо этого поместить логику внутрь onPostExecuted() в (иначе бесполезном) AsyncTask, то представления будут прорисованы.

Пожалуйста, смотрите код ниже, где это дополнительно описано в комментариях.Метод обратного вызова называется onWidthAvailable() и вызывается из FrameLayout.onSizeChanged().

Я также пытался вызвать invalidate() в более поздний произвольный момент времени, но безуспешно.

public class ImageFragment extends Fragment implements FragmentRedrawable, OnLongClickListener, OnWidthAvailableListener {

...

/*
 * This will, among other things, inflate mPlanningView (A DragArea which extends FrameLayout). This is the FrameLayout that will contain the childs.
 */
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    final LinearLayout view = (LinearLayout) inflater.inflate(R.layout.fragment_image, null);
    final ImageView image = (ImageView) view.findViewById(R.id.house_planning);
    image.setImageResource(R.drawable.houseplanning);
    mPlanningView = (com.doffman.dragarea.DragArea) view.findViewById(R.id.imageview);
    image.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                mPlanningView.invalidate();
                setTouchPoint((int) event.getX(), (int) event.getY());
                Log.d(Constants.TAG, String.format("Point (%f, %f)", event.getX(), event.getY()));
            }
            return false;
        }
    });
    image.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {

        @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
            Log.d(Constants.TAG, "Show context menu for point: " + mTouchPoint.x + ", " + mTouchPoint.y);
            MenuInflater inflater = new MenuInflater(getActivity());
            inflater.inflate(R.menu.image_context_menu, menu);

        }
    });

    mPlanningView.addDragListener(mPlanningView, new OnDragListener() {

        @Override
        public void onDrag(View view, final DragEvent dragEvent) {
            switch (dragEvent.getAction()) {
            case com.doffman.dragarea.DragEvent.ACTION_DRAG_STARTED:
                mActivity.disallowViewPagerInterception(true);
                break;
            case com.doffman.dragarea.DragEvent.ACTION_DROP:
                final Bundle data = dragEvent.getBundle();
                final String tag = data.getString("tagImageItem");
                final Integer itemType = data.getInt("tagItemType");
                final Integer itemId = data.getInt("tagItemId");
                final View v = mPlanningView.findViewWithTag(tag);
                if (v == null)
                    return;
                animate(v).setDuration(0).x(dragEvent.getX()).y(dragEvent.getY());
                mDb.setImageItemCoordinates(1, itemType, itemId, dragEvent.getX(), dragEvent.getY());
                getActivity().runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        moveView(v, dragEvent.getX(), dragEvent.getY());
                    }
                });
                break;
            case com.doffman.dragarea.DragEvent.ACTION_DRAG_ENDED:
                mActivity.disallowViewPagerInterception(false);
                break;
            default:
                break;
            }
        }
    });

    mPlanningView.getLocationOnScreen(mPlanningViewCoordinates);
    Log.d(Constants.TAG, "onCreateView: Width: " + mPlanningView.getWidth());
    mPlanningView.setOnWidthAvailableListener(this);
    return view;
}

/*
 * This is called from mPlanningView.onSizeChanged() (i.e. when the width of
 * mPlanningView has been calculated)
 * 
 * This code will read information from SQLite and create Views that will be placed using
 * coordinates defined in the database.
 */
@Override
public void onWidthAvailable() {
    Log.d(Constants.TAG, "onWidthAvailable: Width is " + mPlanningView.getWidth());
    if (mPlanningView.getWidth() == 0)
        return;
    mPlanningView.removeOnWidthAvailableListener();

    /* The most natural thing would be to just do the logic directly. This will indeed add the
     * views to mPlanningView but they are never drawn/seen.
    Log.d(Constants.TAG, "onWidthAvailable thread: " + Thread.currentThread().getId());
    List<ImageLayoutItem> items = mDb.getItemsForImagePage(1);
    for (final ImageLayoutItem item : items) {
        placeEntity(item);
    }   
    */

    /* But if the logic is instead placed in an AsyncTask, the views are drawn */
    new AsyncTask<Void, Void, Void>() {

        @Override
        protected Void doInBackground(Void... params) {
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            Log.d(Constants.TAG, "onPostExecute: UI thread: " + Thread.currentThread().getId());
            List<ImageLayoutItem> items = mDb.getItemsForImagePage(1);
            for (final ImageLayoutItem item : items) {
                placeEntity(item);
            }
        }

    }.execute();

}

private void moveView(View v, float xFactor, float yFactor) {
    int xy[] = translateToAbsoluteCoordinates(xFactor, yFactor);
    animate(v).setDuration(0).x(xy[0]).y(xy[1]);
    FrameLayout.LayoutParams p = (FrameLayout.LayoutParams) v.getLayoutParams();
    p.leftMargin = xy[0];
    p.topMargin = xy[1];
    p.gravity = Gravity.TOP;
    v.setLayoutParams(p);
    mPlanningView.invalidate();
}

private int[] translateToAbsoluteCoordinates(float xFactor, float yFactor) {
    int xy[] = new int[2];
    float width = (float) mPlanningView.getWidth();
    float relFloat = width * xFactor;
    int relativeX = (int) (relFloat);
    int relativeY = (int) ((float) mPlanningView.getHeight() * yFactor);
    xy[0] = mPlanningViewCoordinates[0] + relativeX;
    xy[1] = mPlanningViewCoordinates[1] + relativeY;
    return xy;
}

public void placeEntity(ImageLayoutItem item) {
    LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
    View itemLayout = inflater.inflate(item.getEntity().getItemLayoutId(prefs), null);
    itemLayout.setTag(item.getEntity().getItemTypeId() + ":" + item.getEntity().getId());
    itemLayout.setTag(R.id.tagItemType, item.getEntity().getItemTypeId());
    itemLayout.setTag(R.id.tagItemId, item.getEntity().getId());
    item.getEntity().setView(getActivity(), prefs, mProgressViewer, itemLayout, this);
    if (!item.isShowTitle()) {
        View entityName = itemLayout.findViewById(R.id.entityName);
        if (entityName != null)
            entityName.setVisibility(View.GONE);
    }
    if (item.isUseBackground()) {
        itemLayout.setBackgroundColor(item.getBackgroundColor());
    }

    FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
    itemLayout.setLayoutParams(params);
    mPlanningView.addView(itemLayout, params);

    moveView(itemLayout, item.getXFactor(), item.getYFactor());

    itemLayout.setOnLongClickListener(this);

}

...

}
...