Android 将布局文件放在服务器上,动态改变布局。

2023-11-15

转自:https://blog.csdn.net/chan1116/article/details/44200405

目前在做项目时候有这样的需求:布局文件的控件类型大致相同,例如某布局文件由GridView、ScrollView、TextView、Button四个控件组成,但是控件的摆放位置不同。因为摆放的方式很多,不可能把所有摆放方式都写一个布局文件,因为这样不利于迭代开发。这时候就想出能不能把布局文件放在服务器上,当需要某布局的时候,从服务器下载布局文件保存到存储卡上,然后读取存储卡上的布局文件。

思路大致清除了,那么现在开始进行可行性分析。

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

上面的代码太熟悉不过了,创建一个新Android工程,这玩意就已经写好了!通过setContentView()方法,我们可以设置布局文件,但是这里的参数是R文件的布局ID,如果是布局文件的路径就好了,这样就可以直接读取SD卡的XML布局文件了!按住Ctrl+鼠标左键,点击setContentView()进入:

        /**
         * Set the activity content from a layout resource.  The resource will be
         * inflated, adding all top-level views to the activity.
         *
         * @param layoutResID Resource ID to be inflated.
         *
         * @see #setContentView(android.view.View)
         * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
         */
        public void setContentView(int layoutResID) {
            getWindow().setContentView(layoutResID);
            initActionBar();
        }

从这里我们可以看到,setContentView()方法调用了Window类里的setContentView()方法,调来调去啊!继续按住Ctrl+鼠标左键,点击setContentView()进入:

        /**
         * Convenience for
         * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
         * to set the screen content from a layout resource.  The resource will be
         * inflated, adding all top-level views to the screen.
         *
         * @param layoutResID Resource ID to be inflated.
         * @see #setContentView(View, android.view.ViewGroup.LayoutParams)
         */
        public abstract void setContentView(int layoutResID);

花擦,这里变成了抽象函数,在哪里实现啊!不要惊慌,通过源码可以得知,Window类是一个抽象类,那么它的实现应该是在子类中。那么Window类有哪些子类?

    /**
     * Abstract base class for a top-level window look and behavior policy.  An
     * instance of this class should be used as the top-level view added to the
     * window manager. It provides standard UI policies such as a background, title
     * area, default key processing, etc.
     *
     * <p>The only existing implementation of this abstract class is
     * android.policy.PhoneWindow, which you should instantiate when needing a
     * Window.  Eventually that class will be refactored and a factory method
     * added for creating Window instances without knowing about a particular
     * implementation.
     */

这是Window类源码的开头说明,从中可以立马看到PhoneWindow,对!没错就是它!在PhoneWindow中终于找到了它的具体实现!

        @Override
        public void setContentView(int layoutResID) {
            if (mContentParent == null) {
                installDecor();
            } else {
                mContentParent.removeAllViews();
            }
           mLayoutInflater.inflate(layoutResID, mContentParent);
           final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
        }

从标红的代码,我们可以看到,布局的ID是用在了该段代码,看到了inflate方法,我想大家应该不陌生,在BaseAdpter中经常会用到,用来填充布局的!继续按住Ctrl+鼠标左键,点击inflate(....)进入:

        /**
         * Inflate a new view hierarchy from the specified xml resource. Throws
         * {@link InflateException} if there is an error.
         *
         * @param resource ID for an XML layout resource to load (e.g.,
         *        <code>R.layout.main_page</code>)
         * @param root Optional view to be the parent of the generated hierarchy.
         * @return The root View of the inflated hierarchy. If root was supplied,
         *         this is the root View; otherwise it is the root of the inflated
         *         XML file.
         */
        public View inflate(int resource, ViewGroup root) {
            return inflate(resource, root, root != null);
        }


继续按住Ctrl+鼠标左键,点击return中的inflate(....)进入:

        /**
         * Inflate a new view hierarchy from the specified xml resource. Throws
         * {@link InflateException} if there is an error.
         *
         * @param resource ID for an XML layout resource to load (e.g.,
         *        <code>R.layout.main_page</code>)
         * @param root Optional view to be the parent of the generated hierarchy (if
         *        <em>attachToRoot</em> is true), or else simply an object that
         *        provides a set of LayoutParams values for root of the returned
         *        hierarchy (if <em>attachToRoot</em> is false.)
         * @param attachToRoot Whether the inflated hierarchy should be attached to
         *        the root parameter? If false, root is only used to create the
         *        correct subclass of LayoutParams for the root view in the XML.
         * @return The root View of the inflated hierarchy. If root was supplied and
         *         attachToRoot is true, this is root; otherwise it is the root of
         *         the inflated XML file.
         */
        public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
            if (DEBUG) System.out.println("INFLATING from resource: " + resource);
                XmlResourceParser parser = getContext().getResources().getLayout(resource);
            try {
                return inflate(parser, root, attachToRoot);
            } finally {
                parser.close();
            }
        }

重点来了,看到标红代码,我们可以看到布局ID被getLayout方法作为参数使用,最后返回了XmlResourceParser这玩意,那么它是啥?点击它进去看看:

    /**
     * The XML parsing interface returned for an XML resource.  This is a standard
     * XmlPullParser interface, as well as an extended AttributeSet interface and
     * an additional close() method on this interface for the client to indicate
     * when it is done reading the resource.
     */
    public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {
        /**
         * Close this interface to the resource.  Calls on the interface are no
         * longer value after this call.
         */
        public void close();
    }


wow,它是一个接口,而且是继承自XmlPullParser,那么XmlPullParser是什么?查看它的源码,这里就不贴出源码的英文介绍,因为巴拉巴拉太长了!这里只要知道,它是解析XML文件要使用到的。到这里,我想大概有思路了,因为Android是支持解析Xml文件的,例如游戏的配置文件可以用XML文件完成。如果我们能够将SD卡上的布局文件转成XmlPullParser的话就应该可以完成这种需求!

最后来看看inflate方法的最终实现代码:

    /**
         * Inflate a new view hierarchy from the specified XML node. Throws
         * {@link InflateException} if there is an error.
         * <p>
         * <em><strong>Important</strong></em>   For performance
         * reasons, view inflation relies heavily on pre-processing of XML files
         * that is done at build time. Therefore, it is not currently possible to
         * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
         *
         * @param parser XML dom node containing the description of the view
         *        hierarchy.
         * @param root Optional view to be the parent of the generated hierarchy (if
         *        <em>attachToRoot</em> is true), or else simply an object that
         *        provides a set of LayoutParams values for root of the returned
         *        hierarchy (if <em>attachToRoot</em> is false.)
         * @param attachToRoot Whether the inflated hierarchy should be attached to
         *        the root parameter? If false, root is only used to create the
         *        correct subclass of LayoutParams for the root view in the XML.
         * @return The root View of the inflated hierarchy. If root was supplied and
         *         attachToRoot is true, this is root; otherwise it is the root of
         *         the inflated XML file.
         */
        public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
            synchronized (mConstructorArgs) {
                final AttributeSet attrs = Xml.asAttributeSet(parser);
                Context lastContext = (Context)mConstructorArgs[0];
                mConstructorArgs[0] = mContext;
                View result = root;
     
                try {
                    // Look for the root node.
                    int type;
                    while ((type = parser.next()) != XmlPullParser.START_TAG &&
                            type != XmlPullParser.END_DOCUMENT) {
                        // Empty
                    }
     
                    if (type != XmlPullParser.START_TAG) {
                        throw new InflateException(parser.getPositionDescription()
                                + ": No start tag found!");
                    }
     
                    final String name = parser.getName();
                    
                    if (DEBUG) {
                        System.out.println("**************************");
                        System.out.println("Creating root view: "
                                + name);
                        System.out.println("**************************");
                    }
     
                    if (TAG_MERGE.equals(name)) {
                        if (root == null || !attachToRoot) {
                            throw new InflateException("<merge /> can be used only with a valid "
                                    + "ViewGroup root and attachToRoot=true");
                        }
     
                        rInflate(parser, root, attrs, false);
                    } else {
                        // Temp is the root view that was found in the xml
                        View temp;
                        if (TAG_1995.equals(name)) {
                            temp = new BlinkLayout(mContext, attrs);
                        } else {
                            temp = createViewFromTag(root, name, attrs);
                        }
     
                        ViewGroup.LayoutParams params = null;
     
                        if (root != null) {
                            if (DEBUG) {
                                System.out.println("Creating params from root: " +
                                        root);
                            }
                            // Create layout params that match root, if supplied
                            params = root.generateLayoutParams(attrs);
                            if (!attachToRoot) {
                                // Set the layout params for temp if we are not
                                // attaching. (If we are, we use addView, below)
                                temp.setLayoutParams(params);
                            }
                        }
     
                        if (DEBUG) {
                            System.out.println("-----> start inflating children");
                        }
                        // Inflate all children under temp
                        rInflate(parser, temp, attrs, true);
                        if (DEBUG) {
                            System.out.println("-----> done inflating children");
                        }
     
                        // We are supposed to attach all the views we found (int temp)
                        // to root. Do that now.
                        if (root != null && attachToRoot) {
                            root.addView(temp, params);
                        }
     
                        // Decide whether to return the root that was passed in or the
                        // top view found in xml.
                        if (root == null || !attachToRoot) {
                            result = temp;
                        }
                    }
     
                } catch (XmlPullParserException e) {
                    InflateException ex = new InflateException(e.getMessage());
                    ex.initCause(e);
                    throw ex;
                } catch (IOException e) {
                    InflateException ex = new InflateException(
                            parser.getPositionDescription()
                            + ": " + e.getMessage());
                    ex.initCause(e);
                    throw ex;
                } finally {
                    // Don't retain static reference on context.
                    mConstructorArgs[0] = lastContext;
                    mConstructorArgs[1] = null;
                }
     
                return result;
            }
        }

最后通过该方法,就可以返回一个View,该View相当于整个布局(因为布局也是View)。通过返回的View,就可以进行findViewById的操作了!

关于从SD卡读取XML文件的网上例子有很多,下面项目中用到的方法,用来得到XmlPullParser。

        public XmlPullParser getXmlPullParser(String resource) {
     
            XmlPullParser parser = Xml.newPullParser();
            try {
                FileInputStream is = new FileInputStream(resource);
                parser.setInput(is, "utf-8");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return parser;
        }

这里传入的就是XML在SD卡中的路径,通过相关操作就可以得到XmlPullParser对象。答题思路清晰明了,接下来说说如何做?

首先我们要把Android中的LayoutInflate类拷贝一份到自己工程中,接下来的工作就是照葫芦画瓢了!看下图:


从上图,可以看到LayoutInflate中的所有inflate方法,可惜就是没有以String 作为参数的方法,那么这里我们就按照源码中inflate方法进行增加,如下:

        public View inflate(String resource, ViewGroup root) {
            return inflate(resource, root, root != null);
        }

接下来还有

        public View inflate(String resource, ViewGroup root, boolean attachToRoot) {
            XmlPullParser parser = getXmlPullParser(resource);
            return inflate(parser, root, attachToRoot);
        }


最后就可以调到系统原有的以XmlPullPaser作为参数的inflate方法。那么这里就完成了吗?其实没有,这里做的操作仅仅只是读取布局文件操作。里面的控件如何识别,控件的属性怎么操作,都需要继续研究。XML文件里面有布局,也有控件,若SD卡上的XML文件有某控件,那么我们就要重写该控件。因为这里读取布局的逻辑是我们自己写的,那么相应的View的操作也要自己重写。具体原因将在后续的某期博客中进行分析。例如XML文件中有Button控件:

            <Button
                android:id="@+id/btn_back"
                android:layout_width="@dimen/mine_back_btn_width"
                android:layout_height="@dimen/mine_back_btn_height"
                android:layout_gravity="center_vertical"
                android:background="@drawable/mine_btn_back" />

例如id,layout_width,layout_height等属性,如果我们不重写Button,那么这些属性都是无用的。因为我们没有进行相应的操作,大家通过阅读Button源码就可以了解AttributeSet的相关知识,有时间在后续博客我也将进行分析。

    public class VAButton extends android.widget.Button {
     
     
        public VAButton(Context context, AttributeSet attrs) {
            super(context);
            setAttributeSet(attrs);
        }
     
        @SuppressWarnings("deprecation")
        public void setAttributeSet(AttributeSet attrs) {
     
            HashMap<String, ParamValue> map = YDResource.getInstance().getViewMap();
     
            int count = attrs.getAttributeCount();
            for (int i = 0; i < count; i++) {
                ParamValue key = map.get(attrs.getAttributeName(i));
                if (key == null) {
                    continue;
                }
                switch (key) {
                case id:
                    this.setTag(attrs.getAttributeValue(i));
                    break;
                case text:
                    String value = YDResource.getInstance().getString(
                            attrs.getAttributeValue(i));
                    this.setText(value);
                    break;
                case ellipsize:
                    if (attrs.getAttributeBooleanValue(i, false)) {
     
                        this.setFocusable(true);
                        this.setFocusableInTouchMode(true);
                        this.setSingleLine(true);
                        this.setEllipsize(TruncateAt.MARQUEE);
                        this.setMarqueeRepeatLimit(1000);
                        this.setSingleLine();
                        this.setHorizontallyScrolling(true);
                        this.requestFocus();
                    }
                    break;
                case fadingEdge:
                    this.setHorizontalFadingEdgeEnabled(attrs
                            .getAttributeBooleanValue(i, false));
                    break;
                case scrollHorizontally:
                    this.setHorizontallyScrolling(attrs.getAttributeBooleanValue(i,
                            false));
                    break;
                case textColor:
                    this.setTextColor(YDResource.getInstance().getIntColor(
                            attrs.getAttributeValue(i)));
                    break;
                case textSize:
                    String val1 = attrs.getAttributeValue(i);
                    if (!TextUtils.isEmpty(val1)) {
                        this.setTextSize(YDResource.getInstance()
                                .calculateRealSize(val1));
                    }
                    break;
                case visibility:
                    String val2 = attrs.getAttributeValue(i);
                    if (!TextUtils.isEmpty(val2)) {
                        if (val2.equals("invisible")) {
                            this.setVisibility(View.INVISIBLE);
                        } else if (val2.equalsIgnoreCase("gone")) {
                            this.setVisibility(View.GONE);
                        }
                    }
                    break;
                case background:
                    String backgroundString = attrs.getAttributeValue(i);
                    if (backgroundString.startsWith("#")) {
                        this.setBackgroundColor(YDResource.getInstance()
                                .getIntColor(attrs.getAttributeValue(i)));
                    } else {
                        if (backgroundString.startsWith("@drawable/")) {
                            backgroundString = backgroundString.substring(10);
                        }
                        String rootpath = getContext().getFilesDir().toString();
                        StringBuilder sb = new StringBuilder();
                        sb.append(rootpath).append("/").append(backgroundString)
                                .append(".png");
     
                        Bitmap bm = BitmapFactory.decodeFile(sb.toString());
                        setBackgroundDrawable(new BitmapDrawable(bm));
                    }
                    break;
                case textStyle:
                    if ("bold".equalsIgnoreCase(attrs.getAttributeValue(i)))
                        this.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
                    break;
                case style:
                    String style = attrs.getAttributeValue(i);
                    style = style.substring(style.indexOf("/") + 1);
     
                    Log.i("button", "设置属性值");
                    int id = YDResource.getInstance().getIdentifier(
                            "R.style." + style);
                    this.setTextAppearance(getContext(), id);
                    break;
                case src:
     
                    break;
                case contentDescription:
                    String content = attrs.getAttributeValue(i);
                    this.setContentDescription(content);
                    break;
                case gravity:
                    this.setGravity(Gravity.CENTER_HORIZONTAL);
                    break;
                default:
                    break;
                }
            }
        }
     
    }

通过上面代码,我们可以知道,这里的操作都是对各种属性标签利用代码来进行设置。如果有些复杂的属性不想写了,其实最后还是可以通过代码来完成的。这里就是你的布局需要多少控件属性,就要在代码中完成多少属性的设置。布局控件的代码,以FrameLayout为例:

    public class VAFrameLayout extends android.widget.FrameLayout {
     
     
     
        public VAFrameLayout(Context context, AttributeSet attrs) {
            super(context);
            setLayoutParams(generateLayoutParams(attrs));
        }
     
        @SuppressWarnings({ "unchecked", "deprecation" })
        @Override
        public LayoutParams generateLayoutParams(AttributeSet attrs) {
            // TODO Auto-generated method stub
            LayoutParams params = this.generateDefaultLayoutParams();
            HashMap<String, ParamValue> map = YDResource.getInstance()
                    .getLayoutMap();
            params.width = -2;
            params.height = -2;
            int count = attrs.getAttributeCount();
            for (int i = 0; i < count; i++) {
                String name = attrs.getAttributeName(i);
                ParamValue key = map.get(name);
                if (key == null) {
                    continue;
                }
                switch (key) {
                case layout_width:
                    String width = attrs.getAttributeValue(i);
                    if (width.startsWith("f") || width.startsWith("m")) {
                        params.width = LayoutParams.MATCH_PARENT;
                        break;
                    }
                    if (width.startsWith("w")) {
                        params.width = LayoutParams.WRAP_CONTENT;
                        break;
                    }
                    params.width = YDResource.getInstance()
                            .calculateRealSize(width);
                    break;
                case layout_height:
                    String height = attrs.getAttributeValue(i);
                    if (height.startsWith("f") || height.startsWith("m")) {
                        params.width = -1;
                        break;
                    }
                    if (height.startsWith("w")) {
                        params.width = -2;
                        break;
                    }
                    params.height = YDResource.getInstance().calculateRealSize(
                            height);
                    break;
                case layout_gravity:
     
                    params.gravity = YDResource.getInstance().getGravity(
                            attrs.getAttributeValue(i));
     
                    break;
                case layout_marginLeft:
                    params.leftMargin = YDResource.getInstance().calculateRealSize(
                            attrs.getAttributeValue(i));
                    break;
                case layout_margin:
                    int tm = YDResource.getInstance().calculateRealSize(
                            attrs.getAttributeValue(i));
                    params.setMargins(tm, tm, tm, tm);
                    break;
     
                default:
                    break;
                }
            }
            return params;
        }
     
    }


好了,今天的分析就到这里!

..................................................................................................................................

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 将布局文件放在服务器上,动态改变布局。 的相关文章

  • Qt之QGraphicsView实战篇

    作者 billy 版权声明 著作权归作者所有 商业转载请联系作者获得授权 非商业转载请注明出处 前言 前面的章节介绍了 Graphics View 绘图架构 终于到实战了 真的是千呼万唤始出来 这一章节就用 Graphics View 绘图
  • 第二章:Matplotlib之艺术画笔见乾坤

    第二章 艺术画笔见乾坤 一 概述 1 matplotlib的三层api matplotlib的原理或者说基础逻辑是 用Artist对象在画布 canvas 上绘制 Render 图形 就和人作画的步骤类似 准备一块画布或画纸 准备好颜料 画
  • Tomcat重启单个服务

    一个tomcat中有多个服务 每次为了重启某个服务 需要重新启动Tomcat 会影响其他服务的正常运行 可通过以下方式进行设置 进入Tomcat服务管理页面 对单个服务进行管理 1 首先我们启动tomcat 访问主页 可以看到右上位置有三个
  • MYSQL--基础--10--慢查询日志

    MYSQL 基础 10 慢查询日志 1 是什么 是MySQL提供的一种日志记录 支持将日志记录写入文件 将SQL查询时间超过 大于 设置阈值的语句 记录到慢查询日志中 2 查看语句 2 1 慢日志是否开启和日志文件位置 show varia
  • 【yolo】yolov5-seg实例分割标签

    文章目录 前言 yolo标签 yolov5 seg标签 转换代码 前言 不同于以往的yolo数据 用的是目标框的label 再加上语义标签 yolov5 seg中用的标签只有一个语义标签 没有目标框的标签 具体对比如下 yolo标签 1 目
  • java 获取月份 几周_java – 我想在特定的月份获得几周的时间

    Calendar类的WEEK OF YEAR属性可能对您有用 参考 Calendar class 创建一个新的日期 将是一个月的第一天 得到这一天的一周的这个星期 让你说起点价值 创建一个新的日期 这将是给定月份的最后一天 获得今年的一周
  • FastCFS核心组件FastStore架构及特点

    FastCFS核心组件FastStore架构及特点 本篇文章转载于 FastCFS 作者 余庆 大佬的 FastDFS分享与交流 公众号 上一篇文章介绍了 FastCFS 服务端两大核心组件 FastDIR 和 FastStore 其中 F
  • python文本数据处理_Python文本数据分析与处理

    Python文本数据分析与处理 新闻摘要 分词 使用jieba分词 注意lcut只接受字符串 过滤停用词 TF IDF得到摘要信息或者使用LDA主题模型 TF IDF有两种 jieba analyse extract tags conten
  • Linux中su、su -和sudo的区别

    su 切换到root用户 但是并没有转到root用户家目录下 即没有改变用户的环境 su 切换到root用户 并转到root用户的家目录下 即改变到了root用户的环境 这个涉及到不同用户下的环境变量的配置 sudo 通过sudo 我们能把
  • 【C++】迭代器

    目录 1 迭代器 1 1 迭代器的操作 1 2 迭代器范围 1 3 使用左闭合范围蕴含的编程假定 2 begin和end成员 3 容器操作可能使迭代器失效 3 1 迭代器失效 3 2 编写改变容器的循环程序 3 3 不要保存end返回的迭代

随机推荐

  • (1)分类算法

    分类算法原理 一 KNN K 近邻 1 定义 如果待推测点 空心点 在中间的某个位置 则计算出与其最邻近的4个样本点 K 4 而此时这4个样本点包含了3个类别 1红 1蓝 2绿 针对这样的情况 knn算法通常采用投票法来进行类别推测 即找出
  • 安装VMware Tools方法(对于 18.04LTS版本)

    大家都知道 每一种虚拟机 如VMware Parallels Desktop等都有一个Tools用来让linux系统和windows系统可以共用剪贴板等工具 使得在windows上复制了的东西 在linux ubuntu 上可以访问 对于V
  • MQ知识梳理

    常见MQ有哪几种 分别适用什么场景 常见的有ActiveMQ RabbitMQ RocketMQ Kafka ActiveMQ比较成熟 功能多 但也比较老 各方面都不突出 目前已很少使用 RabbitMQ性能高 功能多 吞度量万级 有开源活
  • C#中的接口(interface)

    接口的命名规范 I 名词 接口与抽象类的区别 接口是由抽象类演变而来的 抽象类是未完全实现逻辑的类 其内部可以有抽象成员 也可以有非抽象成员 且子类在覆写抽象成员时 需要修饰符override 而接口是完全未实现逻辑的 其内部只允许存在抽象
  • java报错:com.alibaba.druid.pool.DruidDataSource.info {dataSource-1} inited

    JDBC使用Druid连接池连接数据库的时候 遇到报错 com alibaba druid pool DruidDataSource info dataSource 1 inited 具体报错信息如下 从网页上报错信息 可以看到是获取驱动名
  • 彻底理解vue底层运用的核心函数Object.defineProperty

    一个函数诞生一个框架 vue就是得益于javaScrit的原生函数Object defineProperty而诞生的 那么Object defineProperty到底是什么 它的用法又是怎样的呢 很简单 它就是用来为对象定义属性的 从字面
  • 51单片机串口通信数码管显示

    外部晶振 11 0592MHZ 主控芯片 STC89C52 程序功能 串口工作方式1 8位UART 比特率9600 接收串口数据 数码管以十 进制格式显示 并且把接收到的数据加1后通过串口发出
  • 【Idea】创建包自动分层

    Idea 创建包自动分层 创建Maven 项目时 新建包使得Tomcat查找访问路径时更准确 但是有时包会不分层 如图1 然后我们使用图3的方法取消勾选 使得新建包时自动分层 如图2
  • 华为机试--简单题(一)

    HJ14 字符串排序 知识点 字符串 排序 描述 给定 n 个字符串 请对 n 个字符串按照字典序排列 数据范围 1 n 1000 字符串长度满足1 len 100 输入描述 输入第一行为一个正整数n 1 n 1000 下面n行为n个字符串
  • JAVA基于Slack实现异常日志报警

    一 功能介绍 在我们日常开发中 如果系统在线上环境上 发生异常 开发人员不能及时知晓来修复 可能会造成重大的损失 因此后端服务中加入异常报警的功能是十分必要的 而开发一个功能全面的异常报警服务 可能会花费较长的周期 今天给大家带来一种基于S
  • STM32F407基于RT-Thread连接ESP8266WiFi模块

    1 连接规则 STM32F4连接ESP8266无线通信 串口通信 首先 本次用到两个串口 我使用的是普中STM32F407 第一个串口为USART1 PA2 PA3 串口一 就是数据线连接单片机和电脑时用的口 串口三USART3 PB10T
  • 当面试官问你离职原因的时候怎么回答比较好?

    所有的前提都是建立在有一定的物质基础 当你的一日三餐都成了问题 都需要家庭支持的时候我希望你可以找一份工作 靠自己的本事养活自己从来不丢人 我觉得死要面子活受罪才是真的让你看不起 所有的建议都是建立在我们是普通打工人的前提 大佬是不需要建议
  • C++:二维数组--输出斐波那契数列的前20项

    大家都知道 在数学世界中有很多神奇的数列 斐波那契数列正是众多有规律的数列中的一种 该数列是意大利数学家列昂纳多 斐波那契发现的 他的基本规律是从第三项开始 每一项都等于前两项之和 第一项和第二项都是1 斐波那契数列如下图所示 1 1 2
  • http概述

    目录 概述 Web客户端和服务器 资源 http如何通信 媒体类型 URI 事务 方法 状态码 报文 连接 版本历程 Web的结构组件 代理 缓存 网关 隧道 Agent代理 爬虫 概述 HTTP是现代全球因特网中使用的公共语言 web浏览
  • 11个强大的Visual Studio调试小技巧

    伯乐在线注 我们在 程序员的那些事 微博上推荐了英文原文 感谢 halftone 被禁用了 的热心翻译 简介 调试是软件开发周期中很重要的一部分 它具有挑战性 同时也很让人疑惑和烦恼 总的来说 对于稍大一点的程序 调试是不可避免的 最近几年
  • 人工智能技术在软件开发中的应用

    人工智能技术的不断发展和成熟 使得它在软件开发中的应用越来越广泛 人工智能技术的应用可以帮助软件开发人员提高效率 降低成本 增强软件的功能性和可靠性 在本文中 我们将探讨人工智能技术在软件开发中的应用 并且提供一些实际案例 以帮助读者更好地
  • PHP 两个页面跳转,session会失效?

    两个页面都包含以下信息 可是 在A php中设置 SESSION go go 在B php中读出来的 SESSION
  • Pycharm远程连接服务器(实践笔记)

    Pycharm远程连接服务器 实践笔记 1 远程连接服务器 2 配置服务器上的环境 记录一下过程 防止自己隔一段时间又忘了 只有pycharm专业版才能远程连接 搞错了步骤1和2的顺序 然后代码一直不能实现同步 一下午配置了n次都不成功 不
  • java计算算术表达式

    直接上代码 String str 1 0 3 2 1 2 ScriptEngineManager manager new ScriptEngineManager ScriptEngine engine manager getEngineBy
  • Android 将布局文件放在服务器上,动态改变布局。

    转自 https blog csdn net chan1116 article details 44200405 目前在做项目时候有这样的需求 布局文件的控件类型大致相同 例如某布局文件由GridView ScrollView TextVi