Blockly安卓原生图形化编程应用(新增在Fragment实现Blockly)

Google原生的blockly下载地址:https://github.com/google/blockly-android
1.自己创建Brockly工程后,发现运行起来会闪退。最终问题出在所有blockly界面相关的Activity需要在mainfest中定义android:windowSoftInputMode=”stateHidden|adjustPan”
2.所谓的xml转c语言其实是转换成javascript,然后根据generate函数转化的界面显示。
3.自定义block最简单的方法是在block factory 该网址下https://blockly-demo.appspot.com/static/demos/blockfactory/index.html 然后把生成的json放入相关模块的xml文件下,然后将type声明在toolbox中。
4.需要了解自定义block的定义规则。
代码块连接接口定义:
previousStatement 上部接口
nextStatement 下部接口
type:
input_value 右侧接口
input_statement 内部接口
input_dummy 右侧无接口
field_dropdown下拉列表:与 options[[],[]]选择列表连用
field_colour 会显示颜色 伴随 output colour
代码块属性定义:
colour 设置颜色 最大值为360
tooltip 声明代码块的含义
name 可自定义 在toolbox中可写入具体限制和类型
message 显示内容 %1 %2 ……为arg0[]中的内容 以[]包裹
inputsInline 为 true 表示在代码块内部
output 输出值 (colour boolean text)
check 检查类型
5.由于项目需求很多都不符合原生携带的功能,原来的blockly已经被我改的面目全非,具体面向需求的问题如下:
①需求:模块上的填写框需要换成文本框或图片都能显示,图片模块需要设置为res里的drawable文件。field_image就根据src值来判断显示,判断逻辑写在com.google.blockly.android.ui.fieldview.BasicFieldImageView中,相应的BasicFieldInputView继承EditText改为继承Linearlayout,注意改为继承TextView会有bug,点击对应区域会出现很长一段乱码字符。
②整个BlockView的点击事件要写在Dragger处理拖动事件中,就相当于判断ACTION_DOWN和ACTION_UP的时间差,小于指定值触发自定义点击事件。
③设置BlockView颜色,如果按照原生的给出hsv值中的h值去设置颜色会出现颜色较给出颜色偏暗,原因是sv都是默认的值,解决方案:
首先将toolbox中的颜色配置为RGB格式:

然后在依赖包blocklylib-core中修改com.google.blockly.utils.ColorUtils中的parseColor()方法:

 /**
     * Parses a string as an opaque color, either as a decimal hue (example: {@code 330}) using a
     * standard set of saturation and value) or as six digit hex code (example: {@code #BB66FF}).
     * If the string cannot be parsed, a {@link ParseException} is thrown.
     *
     * @param str The input to parse.
     * @param tempHsvArray An optional previously allocated array for HSV calculations.
     * @return The parsed color, in {@code int} form.
     * @throws ParseException
     */
    public static int parseColor(@NonNull String str, @Nullable float[] tempHsvArray)
            throws ParseException {
        Integer result = null;

        char firstChar = str.charAt(0);
        if (firstChar == '#' && str.length() == 7) {
            try {
                //result = Integer.parseInt(str.substring(1, 7), 16);
                float[] hsv = new float[3];
                //如果不来回转换一次,会出现BlockView中其他内容显示不正常。
                Color.RGBToHSV(Integer.parseInt(str.substring(1, 3), 16),
                        Integer.parseInt(str.substring(3, 5), 16),
                        Integer.parseInt(str.substring(5, 7), 16),hsv);
                result = Color.HSVToColor(hsv);
            } catch (NumberFormatException e) {
                throw new ParseException("Invalid hex color: " + str, 0);
            }
            return result;
        } else if (Character.isDigit(firstChar) && str.length() <= 3) {
            try {
                int hue = Integer.parseInt(str);
                result = getBlockColorForHue(hue, tempHsvArray);
            } catch (NumberFormatException e) {
                throw new ParseException("Invalid color hue: " + str, 0);
            }
        }
        // Maybe other color formats? 3 digit hex, CSS color functions, etc.
        return result;
    }
6.新需求:要在slidingmenu里的fragment的实现google blockly的功能,踩了几天的坑,简单总结一下:
①首先写一个基类Fragment将blockly—core库中的AbstractBlocklyActivity中的功能整合进去(这个过程需要把context和activity部分内容重新定义,比较繁琐,但难度不大,这里只贴代码)
package com.tysd.eosthird.base;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.google.blockly.android.BlocklyActivityHelper;
import com.google.blockly.android.codegen.CodeGenerationRequest;
import com.google.blockly.android.codegen.LanguageDefinition;
import com.google.blockly.android.control.BlocklyController;
import com.google.blockly.android.ui.BlockViewFactory;
import com.google.blockly.android.ui.MutatorFragment;
import com.google.blockly.model.BlockExtension;
import com.google.blockly.model.BlocklySerializerException;
import com.google.blockly.model.CustomCategory;
import com.google.blockly.model.DefaultBlocks;
import com.google.blockly.model.Mutator;
import com.google.blockly.utils.BlockLoadingException;
import com.tysd.eosthird.R;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;


/**
 * Created by hxl on 2018/5/24 0024.
 */
public abstract class BaseBlocklyFragment extends BaseFragment {
    /**
     * Per the design guidelines, you should show the drawer on launch until the user manually
     * expands it. This shared preference tracks this.
     */
    private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned";

    private static final String TAG = "AbstractBlocklyActivity";

    protected BlocklyActivityHelper mBlocklyActivityHelper;

    protected ActionBar mActionBar;
    protected DrawerLayout mDrawerLayout;

    // These two may be null if {@link #onCreateAppNavigationDrawer} returns null.
    protected View mNavigationDrawer;
    protected ActionBarDrawerToggle mDrawerToggle;

    private boolean mUserLearnedDrawer;

    //ADD
    public void onLoadWorkspaceFromString(String str) {
        try{
            mBlocklyActivityHelper.loadWorkspaceFromString(str);
        }catch (Exception e){

        }
    }

    /**
     * 获取字符串
     */
    protected String getCodeString() {
        return mBlocklyActivityHelper.toCodeString();
    }


    /**
     * Opens or closes the navigation drawer.
     * @param open Opens the navigation drawer if true and closes it if false.
     */
    public void setNavDrawerOpened(boolean open) {
        boolean alreadyOpen = mDrawerLayout.isDrawerOpen(mNavigationDrawer);
        if (open != alreadyOpen) {
            if (open) {
                mDrawerLayout.openDrawer(mNavigationDrawer);
            } else {
                mDrawerLayout.closeDrawer(mNavigationDrawer);
            }
            restoreActionBar();
        }
    }

    /**
     * Called when the user clicks the save action.  Default implementation delegates handling to
     * {@link BlocklyActivityHelper#saveWorkspaceToAppDir(String)} using
     * {@link #getWorkspaceSavePath()}.
     */
    public void onSaveWorkspace() {
        mBlocklyActivityHelper.saveWorkspaceToAppDirSafely(getWorkspaceSavePath());
    }

    /**
     * Called when the user clicks the load action.  Default implementation delegates handling to
     * {@link BlocklyActivityHelper#loadWorkspaceFromAppDir(String)}.
     */
    public void onLoadWorkspace() {
        mBlocklyActivityHelper.loadWorkspaceFromAppDirSafely(getWorkspaceSavePath());
    }

    /**
     * Called when the user clicks the clear action.  Default implementation resets the
     * workspace, removing all blocks from the workspace, and then calls
     * {@link #onInitBlankWorkspace()}.
     */
    public void onClearWorkspace() {
        getController().resetWorkspace();
        onInitBlankWorkspace();
    }

    /**
     * Saves a snapshot of the workspace to {@code outState}.
     *
     * @param outState The {@link Bundle} to save to.
     */
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        getController().onSaveSnapshot(outState);
    }

    /**
     * @return The {@link BlocklyController} controlling the workspace in this activity.
     */
    public final BlocklyController getController() {
        return mBlocklyActivityHelper.getController();
    }

    /**
     * Create a {@link BlocklyActivityHelper} to use for this Activity.
     */
    protected BlocklyActivityHelper onCreateActivityHelper() {
        return new BlocklyActivityHelper(getBaseActivity(),this);
    }

    /** Propagate lifecycle event to BlocklyActivityHelper. */
    @Override
    public void onStart() {
        super.onStart();
        mBlocklyActivityHelper.onStart();
    }

    /** Propagate lifecycle event to BlocklyActivityHelper. */
    @Override
    public void onPause() {
        super.onPause();
        mBlocklyActivityHelper.onPause();
        onAutosave();
    }

    /** Propagate lifecycle event to BlocklyActivityHelper. */
    @Override
    public void onResume() {
        super.onResume();
        mBlocklyActivityHelper.onResume();

        if (mNavigationDrawer != null) {
            // Read in the flag indicating whether or not the user has demonstrated awareness of the
            // drawer. See PREF_USER_LEARNED_DRAWER for details.
            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getBaseActivity());
            mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
            if (!mUserLearnedDrawer) {
                mDrawerLayout.openDrawer(mNavigationDrawer);
            }
        }
    }

    /** Propagate lifecycle event to BlocklyActivityHelper. */
    @Override
    public void onStop() {
        super.onStop();
        mBlocklyActivityHelper.onStop();
    }
    public LayoutInflater mInflater = null;
    public View mLayout = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (null == mLayout) {
            mLayout = getContentView(inflater);
            boolean loadedPriorInstance = checkAllowRestoreBlocklyState(savedInstanceState)
                    && (getController().onRestoreSnapshot(savedInstanceState) || onAutoload());
            if (!loadedPriorInstance) {
                onLoadInitialWorkspace();
            }
        }
        if (null != mLayout.getParent()) {
            ((ViewGroup) mLayout.getParent()).removeView(mLayout);
        }
        return mLayout;
    }

    @Override
    protected View getContentView(LayoutInflater inflater) {

        mLayout = inflater.inflate(com.google.blockly.android.R.layout.drawers_and_action_bar, null);
        mInflater =inflater;
        onCreateActivityRootView();
        mBlocklyActivityHelper = onCreateActivityHelper();
        if (mBlocklyActivityHelper == null) {
            throw new IllegalStateException("BlocklyActivityHelper is null. "
                    + "onCreateActivityHelper must return a instance.");
        }
        onConfigureTrashIcon();
        resetBlockFactory();  // Initial load of block definitions, extensions, and mutators.
        configureCategoryFactories();  // After BlockFactory; before Toolbox
        reloadToolbox();

        addAction();
        return mLayout;
    }

    protected void onConfigureTrashIcon() {
        View trashIcon = mLayout.findViewById(R.id.blockly_trash_icon);
        if (mBlocklyActivityHelper.getController() != null && trashIcon != null) {
            mBlocklyActivityHelper.getController().setTrashIcon(trashIcon);
        }
    }


    public abstract void addAction();

    /**
     *
     * Returns true if the app should proceed to restore the blockly state from the
     * {@code savedInstanceState} Bundle or the {@link #onAutoload() auto save} file. By default, it
     * always returns true, but Activity developers can override this method to add conditional
     * logic.
     * <p/>
     * This does not prevent the state from saving to a Bundle during {@link #onSaveInstanceState}
     * or saving to a file in {@link #onAutosave()}.
     *
     * @param savedInstanceState The Bundle to restore state from.
     * @return True if Blockly state should be restored. Otherwise, null.
     */
    protected boolean checkAllowRestoreBlocklyState(Bundle savedInstanceState) {
        return true;
    }

    /**
     * Hook for subclasses to load an initial workspace. Default implementation just calls
     * {@link #onInitBlankWorkspace()}.
     */
    protected void onLoadInitialWorkspace() {
        onInitBlankWorkspace();
        getController().closeFlyouts();
    }

    /**
     * Called when an autosave of the workspace is triggered, typically by {@link #onPause()}.
     * By default this saves the workspace to a file in the app's directory.
     */
    protected void onAutosave() {
        try {
            mBlocklyActivityHelper.saveWorkspaceToAppDir(getWorkspaceAutosavePath());
        } catch (FileNotFoundException | BlocklySerializerException e) {
            Log.e(TAG, "Failed to autosaving workspace.", e);
        }
    }

    /**
     * Called when the activity tries to restore the autosaved workspace, typically by
     * {@link #onCreate(Bundle)} if there was no workspace data in the bundle.
     *
     * @return true if a previously saved workspace was loaded, false otherwise.
     */
    protected boolean onAutoload() {
        String filePath = getWorkspaceAutosavePath();
        try {
            mBlocklyActivityHelper.loadWorkspaceFromAppDir(filePath);
            return true;
        } catch (FileNotFoundException e) {
            // No workspace was saved previously.
        } catch (BlockLoadingException | IOException e) {
            Log.e(TAG, "Failed to load workspace", e);
            mBlocklyActivityHelper.getController().resetWorkspace();

            File file = getBaseActivity().getFileStreamPath(filePath);
            if (!file.delete()) {
                Log.e(TAG, "Failed to delete corrupted autoload workspace: " + filePath);
            }
        }
        return false;
    }

    /**
     * Hook for subclasses to initialize a new blank workspace. Initialization may include
     * configuring default variables or other setup.
     */
    protected void onInitBlankWorkspace() {}

    /**
     * @return The name to show in the {@link ActionBar}.  Defaults to the activity name.
     */
    @NonNull
    protected CharSequence getWorkspaceTitle() {
        return getBaseActivity().getTitle();
    }

    /**
     * @return The asset path for the xml toolbox config.
     */
    @NonNull
    abstract protected String getToolboxContentsXmlPath();

    /**
     * @return The asset path for the json block definitions.
     */
    @NonNull
    abstract protected List<String> getBlockDefinitionsJsonPaths();

    /**
     * @return The asset path for the core language file used to generate code.
     */
    @NonNull
    protected LanguageDefinition getBlockGeneratorLanguage() {
        return DefaultBlocks.LANGUAGE_DEFINITION;
    }

    /**
     * This method provides a hook to register {@link BlockExtension}s that support the block
     * definitions in this activity. By default, it adds all extensions in
     * {@link DefaultBlocks#getExtensions() DefaultBlocks} to the block factory, via the
     * {@link #onCreateActivityHelper() BlocklyActivityHelper}
     * {@link BlocklyActivityHelper#configureExtensions() implementation}.
     * <p/>
     * Extensions with the same key will replace existing extensions, so it is safe
     * to call super and then update specific extensions.
     * <p/>
     * Called from {@link #resetBlockFactory()}.
     */
    protected void configureBlockExtensions() {
        mBlocklyActivityHelper.configureExtensions();
    }

    /**
     * This method provides a hook to register {@link Mutator.Factory}s and
     * {@link MutatorFragment.Factory}s that support the block definitions in this activity. By
     * default, it adds the mutators in {@link DefaultBlocks#getMutators() DefaultBlocks} to the
     * BlockFactory, via the {@link #onCreateActivityHelper() BlocklyActivityHelper}
     * {@link BlocklyActivityHelper#configureMutators() implementation}.
     * <p/>
     * Mutators with the same key will replace existing mutators, so it is safe
     * to call super and then update specific mutators.
     * <p/>
     * Called from {@link #resetBlockFactory()}.
     */
    protected void configureMutators() {
        mBlocklyActivityHelper.configureMutators();
    }

    /**
     * This method provides a hook to register custom {@link CustomCategory}s that support
     * the toolboxes in this activity. By default, it registers the categories in
     * {@link DefaultBlocks}, via the {@link #onCreateActivityHelper() BlocklyActivityHelper}
     * {@link BlocklyActivityHelper#configureMutators() implementation}.
     * <p/>
     * Category factories with the same {@code custom} key will replace existing
     * {@link CustomCategory}s, so it is safe to call super and then update specific categories.
     * <p/>
     * Called once at activity creation.
     */
    protected void configureCategoryFactories() {
        mBlocklyActivityHelper.configureCategoryFactories();
    }

    /**
     * Returns the asset file paths to the generators (JS files) to use for the most
     * recently requested "Run" action. Called from {@link #onRunCode()}. This is expected to be a
     * list of JavaScript files that contain the block generators.
     *
     * @return The list of file paths to the block generators.
     */
    @NonNull
    abstract protected List<String> getGeneratorsJsPaths();

    /**
     * Returns a generation callback to use for the most recently requested "Run" action.
     * Called from {@link #onRunCode()}.
     *
     * @return The generation callback.
     */
    @NonNull
    abstract protected CodeGenerationRequest.CodeGeneratorCallback getCodeGenerationCallback();

    /**
     * @return The path to the saved workspace file on the local device. By default,
     *         "workspace.xml".
     */
    @NonNull
    protected String getWorkspaceSavePath() {
        return "workspace.xml";
    }

    /**
     * @return The path to the automatically saved workspace file on the local device. By default,
     *         "autosave_workspace.xml".
     */
    @NonNull
    protected String getWorkspaceAutosavePath() {
        return "autosave_workspace.xml";
    }

    /**
     * Creates or loads the root content view (by default, {@link com.google.blockly.android.R.layout#drawers_and_action_bar})
     * for the Activity.  It is also responsible for assigning {@link #mActionBar} and
     * {@link #mDrawerLayout}, and adding the view returned by {@link #onCreateContentView}.
     */
    protected void onCreateActivityRootView() {

        mDrawerLayout = (DrawerLayout) mLayout.findViewById(com.google.blockly.android.R.id.drawer_layout);

        mActionBar = getBaseActivity().getSupportActionBar();
        //mActionBar.setDisplayShowTitleEnabled(true);

        // Create and attach content view into content container.  If content is a fragment, content
        // will be null here and the container will be populated during the FragmentTransaction.
        View content = onCreateContentView(com.google.blockly.android.R.id.content_container);
        if (content != null) {
            FrameLayout contentContainer = (FrameLayout) mLayout.findViewById(com.google.blockly.android.R.id.content_container);
            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            if (content.getParent() != contentContainer) {
                contentContainer.addView(content, lp);
            } else {
                content.setLayoutParams(lp);
            }
        }

        mNavigationDrawer = onCreateAppNavigationDrawer();
        if (mNavigationDrawer != null) {
            setupAppNaviagtionDrawer();
        }
    }

    /**
     * Constructs (or inflates) the primary content view of the Activity.
     *
     * @param containerId The container id to target if using a {@link Fragment}
     * @return The {@link View} constructed. If using a {@link Fragment}, return null.
     */
    protected View onCreateContentView(int containerId) {
        return mInflater.inflate(com.google.blockly.android.R.layout.blockly_unified_workspace, null);
    }

    /**
     * @return The {@link View} to be used for the navigation menu. Otherwise null.
     */
    protected View onCreateAppNavigationDrawer() {
        return null;
    }

    /**
     * Configures the activity to support a navigation menu and drawer provided by
     * {@link #onCreateAppNavigationDrawer}.
     */
    protected void setupAppNaviagtionDrawer() {
        DrawerLayout.LayoutParams lp = new DrawerLayout.LayoutParams(
                getResources().getDimensionPixelSize(com.google.blockly.android.R.dimen.navigation_drawer_width),
                ViewGroup.LayoutParams.MATCH_PARENT,
                Gravity.START);
        // Add navigation drawer above the content view, as the first drawer.
        mDrawerLayout.addView(mNavigationDrawer, 1, lp);

        // set a custom shadow that overlays the main content when the drawer opens
        mDrawerLayout.setDrawerShadow(com.google.blockly.android.R.drawable.drawer_shadow,
                GravityCompat.START);

        mActionBar.setDisplayHomeAsUpEnabled(true);
        mActionBar.setHomeButtonEnabled(true);

        // ActionBarDrawerToggle ties together the the proper interactions
        // between the navigation drawer and the action bar app icon.
        mDrawerToggle = new ActionBarDrawerToggle(getActivity(), mDrawerLayout,
                com.google.blockly.android.R.string.navigation_drawer_open,  /* "open drawer" description for accessibility */
                com.google.blockly.android.R.string.navigation_drawer_close  /* "close drawer" description for accessibility */
        ) {
            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);

                getBaseActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu()
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                if (!mUserLearnedDrawer) {
                    // The user manually opened the drawer; store this flag to prevent auto-showing
                    // the navigation drawer automatically in the future.
                    mUserLearnedDrawer = true;
                    SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
                            getActivity());
                    sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply();
                }

                getBaseActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu()
            }
        };

        // Defer code dependent on restoration of previous instance state.
        mDrawerLayout.post(new Runnable() {
            @Override
            public void run() {
                mDrawerToggle.syncState();
            }
        });
        mDrawerLayout.addDrawerListener(mDrawerToggle);
    }

    /**
     * Runs the code generator. Called when user selects "Run" action.
     * <p/>
     * Gets the latest block definitions and generator code by calling
     * {@link #getBlockDefinitionsJsonPaths()} and {@link #getGeneratorsJsPaths()} just before
     * invoking generation.
     *
     * @see #getCodeGenerationCallback()
     */
    protected void onRunCode() {
        mBlocklyActivityHelper.requestCodeGeneration(
                getBlockGeneratorLanguage(),
                getBlockDefinitionsJsonPaths(),
                getGeneratorsJsPaths(),
                getCodeGenerationCallback());
    }

    /**
     * Restores the {@link ActionBar} contents when the navigation window closes, per <a
     * href="http://developer.android.com/design/material/index.html">Material design
     * guidelines</a>.
     */
    protected void restoreActionBar() {
        ActionBar actionBar = getBaseActivity().getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayShowTitleEnabled(true);
            actionBar.setTitle(getWorkspaceTitle());
        }
    }

    /**
     * Reloads the toolbox contents using the path provided by {@link #getToolboxContentsXmlPath()}.
     */
    protected void reloadToolbox() {
        mBlocklyActivityHelper.reloadToolbox(getToolboxContentsXmlPath());
    }

    /**
     * Reloads the block definitions, including extensions and mutators. Calls
     * {@link #getBlockDefinitionsJsonPaths()} and {@link #configureBlockExtensions()}.
     *
     * @throws IOException If there is a fundamental problem with the input.
     * @throws BlockLoadingException If the definition is malformed.
     */
    protected void resetBlockFactory() {
        mBlocklyActivityHelper.resetBlockFactory(
                getBlockDefinitionsJsonPaths());

        configureBlockExtensions();
        configureMutators();
        configureCategoryFactories();

        // Reload the toolbox?
    }

    /**
     * @return True if the navigation menu was closed and the back event should be consumed.
     *         Otherwise false.
     */
    protected boolean onBackToCloseNavMenu() {
        if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
            mDrawerLayout.closeDrawer(GravityCompat.START);
            return true;
        }
        return false;
    }
}

②简单改写后Fragment中实现blockly操作会有两个问题:第一,拖动带有BasicFieldInputView的控件会有放手后先返回初始位置又到放手的位置,这个做安卓相关的EditText拖动都知道,重写EditText的onDragEvent方法 return false即可。第二,拖至垃圾桶无法删除,这是因为BlocklyActivityHelper的onConfigureTrashIcon()方法中:

protected void onConfigureTrashIcon() {
     View trashIcon = mActivity.findViewById(R.id.blockly_trash_icon);
     if (mController != null && trashIcon != null) {
         mController.setTrashIcon(trashIcon);
     }
}

trashIcon 为空,无法设置拖动监听事件,上面的fragment基类已解决此问题,将该方法改写到fragment基类中:

protected void onConfigureTrashIcon() {
    View trashIcon = mLayout.findViewById(R.id.blockly_trash_icon);
    if (mBlocklyActivityHelper.getController() != null && trashIcon != null) {
        mBlocklyActivityHelper.getController().setTrashIcon(trashIcon);
    }
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,我来为你演示一个创建应用场景的例子。 假设我们正在开发一个新闻客户端,我们需要在主界面中实现多个Fragment的分屏,分别展示不同的新闻分类。 首先,我们需要在布局文件中定义一个ViewPager,设置其高度为match_parent,宽度为wrap_content。然后,我们创建多个Fragment,每个Fragment用于展示不同分类的新闻。 下面是一个示例的布局文件: ```xml <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 接下来,我们创建多个Fragment,每个Fragment用于展示不同分类的新闻。下面是一个示例的Fragment代码: ```java public class NewsFragment extends Fragment { private static final String ARG_CATEGORY = "category"; private String mCategory; public static NewsFragment newInstance(String category) { NewsFragment fragment = new NewsFragment(); Bundle args = new Bundle(); args.putString(ARG_CATEGORY, category); fragment.setArguments(args); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mCategory = getArguments().getString(ARG_CATEGORY); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_news, container, false); // TODO: 在这里添加展示新闻的逻辑代码 return view; } } ``` 在这个示例中,我们使用newInstance()方法创建一个NewsFragment实例,并通过Bundle传递新闻分类的信息。在onCreate()方法中,我们从Bundle中获取新闻分类的信息。在onCreateView()方法中,我们使用LayoutInflater加载布局文件,并在其中添加展示新闻的逻辑代码。 最后,我们需要创建一个PagerAdapter类,用于管理ViewPager中的Fragment。下面是一个示例的PagerAdapter代码: ```java public class NewsPagerAdapter extends FragmentPagerAdapter { private static final int NUM_PAGES = 3; public NewsPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { switch (position) { case 0: return NewsFragment.newInstance("头条"); case 1: return NewsFragment.newInstance("娱乐"); case 2: return NewsFragment.newInstance("体育"); default: return null; } } @Override public int getCount() { return NUM_PAGES; } @Override public CharSequence getPageTitle(int position) { switch (position) { case 0: return "头条"; case 1: return "娱乐"; case 2: return "体育"; default: return null; } } } ``` 在这个示例中,我们创建了一个NewsPagerAdapter类,用于管理ViewPager中的Fragment。在getItem()方法中,我们根据位置返回不同的NewsFragment实例。在getCount()方法中,我们返回Fragment的数量。在getPageTitle()方法中,我们返回每个Fragment的标题。 最后,在MainActivity中,我们将PagerAdapter设置给ViewPager即可: ```java public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewPager viewPager = findViewById(R.id.view_pager); NewsPagerAdapter pagerAdapter = new NewsPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(pagerAdapter); } } ``` 这样,我们就实现了多个Fragment的分屏,每个Fragment展示不同分类的新闻。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值