Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/184.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/spring-boot/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 未绘制FrameLayout中的子对象_Android_Layout_Invalidation_Android Framelayout - Fatal编程技术网

Android 未绘制FrameLayout中的子对象

Android 未绘制FrameLayout中的子对象,android,layout,invalidation,android-framelayout,Android,Layout,Invalidation,Android Framelayout,我在我的Android应用程序中实现了一个功能,用户可以在FrameLayout中动态添加和删除视图,并使用拖放技术以绝对坐标定位视图。用户通常会将楼层规划图像作为背景加载到框架布局,然后添加表示室内灯具的按钮,并在适当的位置对其进行定位 无论如何,我有一个问题,当片段加载时,我需要充气并将视图添加到框架布局。子视图来自SQLite数据库中的信息。为了正确定位子视图(取决于方向),我需要考虑FrameLayout的宽度和高度。因此,我无法在我的片段中的onCreateView()中执行此工作 F

我在我的Android应用程序中实现了一个功能,用户可以在
FrameLayout
中动态添加和删除视图,并使用拖放技术以绝对坐标定位视图。用户通常会将楼层规划图像作为背景加载到
框架布局
,然后添加表示室内灯具的按钮,并在适当的位置对其进行定位

无论如何,我有一个问题,当
片段
加载时,我需要充气并将视图添加到
框架布局
。子视图来自SQLite数据库中的信息。为了正确定位子视图(取决于方向),我需要考虑
FrameLayout
的宽度和高度。因此,我无法在我的
片段中的
onCreateView()
中执行此工作

FrameLayout
实际上是一个
DragArea
(为了简单起见,我仍然将这个对象称为
FrameLayout
),一个扩展
FrameLayout
的自定义类。为了在计算宽度/高度时获得对
片段的回调,我在
FrameLayout
中覆盖
onSizeChanged
,它调用
片段

奇怪的是。如果我在我的
片段
中的回调方法中创建视图并调用
FrameLayout.invalidate
似乎什么都没有发生。检查显示视图已添加到
FrameLayout
,但未显示。如果打开hierarchyviewer并按Load View Hierarchy,将显示视图。因此,这里似乎有一个副作用,吸引了人们的观点。我尝试调用
FrameLayout.requestLayout()
,并通过执行
getActivity().runOnUiThread()
中的逻辑来确保它在UI线程上运行,但这没有什么区别

但是如果我将逻辑放在一个(否则无用的)
AsyncTask
中的
onPostExecuted()
中,则会绘制视图

请参阅下面的代码,注释中对此进行了进一步说明。回调方法名为
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);

}

...

}
公共类ImageFragment扩展了Fragment实现了FragmentRedrawable、OnLongClickListener、OnWidthAvailableListener{
...
/*
*这将使mPlanningView(一种扩展FrameLayout的DragArea)膨胀,这是包含child的FrameLayout。
*/
@凌驾
CreateView上的公共视图(布局、充气机、视图组容器、捆绑包保存状态){
最终线性布局视图=(线性布局)充气器充气(R.layout.fragment_图像,空);
最终ImageView图像=(ImageView)视图。findViewById(R.id.house_planning);
image.setImageResource(R.drawable.houseplanning);
mPlanningView=(com.doffman.dragarea.dragarea)view.findviewbyd(R.id.imageview);
setOnTouchListener(新的OnTouchListener(){
@凌驾
公共布尔onTouch(视图v,运动事件){
if(event.getAction()==MotionEvent.ACTION\u向下){
mPlanningView.invalidate();
setTouchPoint((int)event.getX(),(int)event.getY());
Log.d(Constants.TAG,String.format(“点(%f,%f)”,event.getX(),event.getY());
}
返回false;
}
});
setOnCreateContextMenuListener(新的OnCreateContextMenuListener(){
@凌驾
public void onCreateContextMenu(ContextMenu菜单、视图v、ContextMenuInfo菜单信息){
Log.d(Constants.TAG,“显示点的上下文菜单:“+mTouchPoint.x+”,“+mTouchPoint.y”);
MenuInflater充气器=新的MenuInflater(getActivity());
充气器。充气(右菜单。图像上下文菜单,菜单);
}
});
addDragListener(mPlanningView,新的OnDragListener()){
@凌驾
公共排水孔(视图,最终排水孔排水孔){
开关(dragEvent.getAction()){
案例com.doffman.dragarea.DragEvent.ACTION\u DRAG\u已启动:
mActivity.disallowViewPagerInterception(true);
打破
案例com.doffman.dragarea.DragEvent.ACTION\u DROP:
final Bundle data=dragEvent.getBundle();
最终字符串标记=data.getString(“tagImageItem”);
最终整数itemType=data.getInt(“tagItemType”);
最终整数itemId=data.getInt(“tagItemId”);
最终视图v=mPlanningView.findViewWithTag(标记);
如果(v==null)
回来
设置动画(v).setDuration(0).x(dragEvent.getX()).y(dragEvent.getY());
setImageItemCoordinates(1,itemType,itemId,dragEvent.getX(),dragEvent.getY());
getActivity().runOnUiThread(新的Runnable()){
@凌驾
公开募捐{
moveView(v,dragEvent.getX(),dragEvent.getY());
}
});
打破
案例com.doffman.dragarea.DragEvent.ACTION\u DRAG\u结束:
mActivity.disallowViewPagerInterception(false);
打破
违约:
打破
}
}
});
mPlanningView.getLocationOnScreen(mPlanningViewCoordinates);
d(Constants.TAG,“onCreateView:Width:+mPlanningView.getWidth());
mPlanningView.setOnWidthAvailableListener(此);
返回视图;
}
/*
*这是从mPlanningView.onSizeChanged()调用的(即当
*mPlanningView(已计算)
* 
*此代码将从SQLite读取信息,并创建使用
*定义的坐标i