动手实现天气预报App(一)——数据、工具类和碎片布局准备_android 第一行代码 weather?cityid=1&key=bc0418b57b24918-程序员宅基地

技术标签: java  android  Android  


本系列记录一下跟随guolin大神学习的项目自己完成一个天气预报Android App.

需求分析

  1. 可以列出全国所有省市县
  2. 可以查看全国任意城市天气信息
  3. 可以自由切换城市,查看不同地区天气
  4. 有手动刷新和后台自动更新天气功能

数据来源

郭神提供的数据接口,包含全国省市县名称和编号信息:

省级单位:

http://guolin.tech/api/china

服务器会返回JSON格式数据

在这里插入图片描述

市级单位:在后面加上具体省份id即可

http://guolin.tech/api/china/16

在这里插入图片描述

县级单位以此类推:

http://guolin.tech/api/china/16/116

在这里插入图片描述

接着为了获取每个地区具体的天气情况需要注册和风天气的接口:
在这里插入图片描述

拿到自己App的API KEY

之后配合每个具体地区的weather_id即可查看天气信息,如:

http://guolin.tech/api/weathercityid=cn101190401&key=bc0418b57b2d4918819d3974ac1285d9

返回的数据如:

在这里插入图片描述

数据获取后接着做JSON解析工作即可。

创建数据库和表

第一阶段要做的就是创建好数据库和表,从而将服务器获取到的数据存储到本地。这里使用 LitePal来管理数据库。

首先创建目录结构 ,其中db包用于存放数据库模型相关的代码

gson包用于存放GSON模型相关的代码,

service包用于存放服务相关的代码

util包用于存放工具相关的代码。
在这里插入图片描述

添加相关依赖:
在这里插入图片描述

使用 LiteRal,可以用面向对象的思维来实现数据库相关操作,比如定义一个 Java bean,在Book类中我们定义了id、 author、 price、 pages、name这几个字段,并生成了相应的 getter和 setter方法。Book类就会对应数据库中的Book表,而类中的每一个字段分别对应了表中的每一个列,这就是对象关系映射最直观的体验。

在db下新建省市县三个bean来对应三张表,具体代码如下:

/**
 *省信息表
 */
public class Province  extends DataSupport {
    

    private  int id;//代号
    private String provinceName;//省名
    private int provinceCode;//省编号

    public int getId() {
    
        return id;
    }

    public void setId(int id) {
    
        this.id = id;
    }

    public String getProvinceName() {
    
        return provinceName;
    }

    public void setProvinceName(String provinceName) {
    
        this.provinceName = provinceName;
    }

    public int getProvinceCode() {
    
        return provinceCode;
    }

    public void setProvinceCode(int provinceCode) {
    
        this.provinceCode = provinceCode;
    }
}

LiteRal进行表管理操作时不需要模型类有任何的继承结构,但是进行CRUD操作时就不行了,
必须要继承自 DataSupport类才行,因此这里我们需要把继承结构给加上。

/**
 * 城市信息表
 */
public class City extends DataSupport {
    
    private int id; //字段
    private String cityName; //城市名称
    private int cityCode;   //城市代码
    private int provinceId;//城市所属省份编号


    public int getId() {
    
        return id;
    }

    public void setId(int id) {
    
        this.id = id;
    }

    public String getCityName() {
    
        return cityName;
    }

    public void setCityName(String cityName) {
    
        this.cityName = cityName;
    }

    public int getCityCode() {
    
        return cityCode;
    }

    public void setCityCode(int cityCode) {
    
        this.cityCode = cityCode;
    }

    public int getProvinceId() {
    
        return provinceId;
    }

    public void setProvinceId(int provinceId) {
    
        this.provinceId = provinceId;
    }

}

/**
 * 地区/县信息表
 */
public class County extends DataSupport {
    
        private int id;
        private String countyName;//县名
        private String weatherId;//天气id
        private int cityId;//所属县ID

    public int getId() {
    
        return id;
    }

    public void setId(int id) {
    
        this.id = id;
    }

    public String getCountyName() {
    
        return countyName;
    }

    public void setCountyName(String countyName) {
    
        this.countyName = countyName;
    }

    public String getWeatherId() {
    
        return weatherId;
    }

    public void setWeatherId(String weatherId) {
    
        this.weatherId = weatherId;
    }

    public int getCityId() {
    
        return cityId;
    }

    public void setCityId(int cityId) {
    
        this.cityId = cityId;
    }
}

接下来需要配置``litepal.xml`

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<litepal>
    <dbname value ="MyWeatherApp"/>
    <version value="1"/>
    <list>
        <mapping class="com.wz.myweatherapp.db.County"/>
        <mapping class="com.wz.myweatherapp.db.City"/>
        <mapping class="com.wz.myweatherapp.db.Province"/>
    </list>
</litepal>

其中,< dbname>标签用于指定数据库名,< version>标签用于指定数据库版本号,
标签用于指定所有的映射模型,我们稍后就会用到。

最后还需要再配置一下 LitePalApplication,修改 Androidmanifest xml中的代码,如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.wz.myweatherapp">
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:name="org.litepal.LitePalApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        ......

加载全国省市区数据

请求和解析数据工具类

由于数据是从网络服务端获取的,故可以创建工具类

在这里插入图片描述

public class HttpUtil {
    
    /**
     * 传入请求地址 注册一个回调来处理服务器响应
     * @param address
     * @param callback
     */
    public static void sendOkHttpRequest(String address,Callback callback){
    
        //创建一个OkHttpClient实例
        OkHttpClient client = new OkHttpClient();
        //创建Request来发起HTTP请求
        Request request = new Request.Builder().url(address).build();
        //之后调用OkhttpClient的newCall()方法来创建一个CalL对象,并调用它的execute()方
        //法来发送请求并获取服务器返回的数据,写法如下
        client.newCall(request).enqueue(callback);

    }
}

另外,由于服务器返回的省市县数据都是JSON格式的,所以最好再提供一个工具类来解析和处理这种数据。在util包下新建一个 Utility类,代码如下所示

在这里插入图片描述

public class Utility {
    
    /**
     * 解析处理服务器返回的省级数据
     *
     */
    public static boolean handleProvinceResponse(String response){
    
        if (!TextUtils.isEmpty(response)) {
    
            try {
    
                //可以看到,解析JSON的代码非常简单,由于在服务器中定义的是一个JSON数组,
                //因此这里首先是将服务器返回的数据传入到了一个JSONArray对象中。然后循环遍历这个JSONArray,
                // 从中取出的每一个元素都是一个JSONArray对象,每个JSONArray对象中又会包含id、name和 version这些数据。
                //接下来只需要调用 getstring()方法将这些数据取出,并打印出来即可。
                //先使用JSONArray 和 JSONObject将数据解析
                JSONArray allProvince = new JSONArray(response);
                for (int i = 0; i < allProvince.length(); i++) {
    
                    JSONObject provinceObject = allProvince.getJSONObject(i);
                    //装入实体对象
                    Province province = new Province();
                    province.setProvinceName(provinceObject.getString("name"));
                    province.setProvinceCode(provinceObject.getInt("id"));
                    //由于province 继承了litepal特性 故使用save存储进数据库
                    province.save();
                }
                return true;
            } catch (JSONException e) {
    
                e.printStackTrace();
            }
        }
        return false;
    }
/**
 * 解析处理服务器返回的市级数据
 */
public static boolean handleCityResponse(String response,int provinceId){
    
    try {
    
        if (!TextUtils.isEmpty(response)) {
    
            JSONArray allCities = new JSONArray(response);
            for (int i = 0; i < allCities.length(); i++) {
    
                JSONObject cityObject = allCities.getJSONObject(i);
                City city = new City();
                city.setCityName(cityObject.getString("name"));
                city.setCityCode(cityObject.getInt("id"));
                city.setProvinceId(provinceId);
                city.save();
            }
            return true;
        }
    } catch (JSONException e) {
    
        e.printStackTrace();
    }
    return false;
}
/**
 * 解析处理县级数据
 *
 */
public static boolean handleCountyResponse(String response ,int cityId){
    
    try {
    
        if (!TextUtils.isEmpty(response)) {
    
            JSONArray allCounties = new JSONArray(response);
            for (int i = 0; i < allCounties.length(); i++) {
    
                JSONObject countyObject = allCounties.getJSONObject(i);
                County county  = new County();
                county.setCityId(cityId);
                county.setCountyName(countyObject.getString("name"));
                county.setWeatherId(countyObject.getString("weather_id"));
                county.save();
            }
            return true;
        }
    } catch (JSONException e) {
    
        e.printStackTrace();
    }
    return false;
}

}

需要准备的工具类就这么多,现在可以开始写界面了。由于遍历全国省市县的功能我们在后面还会复用,因此就不写在活动里面了,而是写在碎片里面,这样需要复用的时候直接在布局里面引用碎片就可以了。

碎片界面布局

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#fff">
<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary">
    <TextView
        android:id="@+id/title_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="20sp"
        android:textColor="#fff"
        />
    <Button
        android:id="@+id/back_button"
        android:layout_width="25dp"
        android:layout_height="25dp"
        android:layout_marginLeft="10dp"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:background="@drawable/ic_back"/>
</RelativeLayout>
    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</LinearLayout>

加载数据碎片逻辑

接下来也是最关键的一步,需要编写用于加载省市县数据的碎片了。新建 ChooseAreaFragment继承自 Fragment这个逻辑却不复杂,需要慢慢理一下。在 onCreateview()方法中先是获取到了一些控件的实例,然后去初始化了 Array Adapter,并将它设置为 List view的适配器。接着在 onActivityCreated()方法中给 Listview和Button设置了点击事件,到这里初始化工作就算是完成了。

@Override
 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    
    View view = inflater.inflate(R.layout.choose_area, container, false);
    mTitleText = view.findViewById(R.id.title_text);
    mButton = view.findViewById(R.id.back_button);
    mListView = view.findViewById(R.id.list_view);
    //不过,数组中的数据是无法直接传递给 Listview的,我们还需要借助适配器来完成。 Android
    //中提供了很多适配器的实现类,其中我认为最好用的就是 Array Adapter。它可以通过泛型来指定
    //要适配的数据类型,然后在构造函数中把要适配的数据传入。 Array Adapter有多个构造函数的重
    //载,你应该根据实际情况选择最合适的一种。这里如果提供的数据都是字符串,可以将
    //ArrayAdapter的泛型指定为 String
    // 然后在ArrayAdapter的构造函数中依次传入当前上下文,List view子项布局的id,以及要适配的数据。
    // 注意,使用了simple_list_item_1作为 List view子项布局的id,这是一个 Android内置的布局文件,里面只有一个
    //Text View,可用于简单地显示一段文本。这样适配器对象就构建好了
    //最后,还需要调用 List View的 setAdapter()方法,将构建好的适配器对象传递进去,这样
    //List view和数据之间的关联就建立完成了。
    adapter = new ArrayAdapter<>(getContext(),android.R.layout.simple_list_item_1,dataList);
    mListView.setAdapter(adapter);

    return view;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    
    super.onActivityCreated(savedInstanceState);
    /*
    列表点击事件
     */
    mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    
        //可以看到,我们使用 setonItemClicklistener()方法为 Listview注册了一个监听器,当
        //用户点击了 Listview中的任何一个子项时,就会回调 onItemclick()方法。在这个方法中可以
        //通过 position参数判断出用户点击的是哪一个子项,然后获取到相应的类信息,并通过Toast显示
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int pos, long idl) {
    
            if (currentLevel == LEVEL_PROVINCE){
    
                selectedProvince = provinceList.get(pos);
                queryCity();
            }else if (currentLevel == LEVEL_CITY){
    
                selectedCity = cityList.get(pos);
                queryCounty();
            }
        }
    });
    /*
    返回按钮 点击事件
     */
    mButton.setOnClickListener(new View.OnClickListener() {
    
        @Override
        public void onClick(View view) {
    
            if (currentLevel == LEVEL_COUNTY){
    
                /*
                如果是在县页面返回 则获取市信息
                 */
                queryCity();
            }else if (currentLevel == LEVEL_CITY){
    
                /*
                如果实在市级别返回 则获取省信息
                 */
                queryProvince();
            }
        }
    });

    /*
    创建时默认获取省信息
     */
    queryProvince();
}
/*
查询省内所有市
 */
    private void queryCity() {
    
            mTitleText.setText(selectedProvince.getProvinceName());
            mButton.setVisibility(View.VISIBLE);
            cityList = DataSupport.where("provinceid  = ?",String.valueOf(selectedProvince.getId())).find(City.class);
        if (cityList.size()>0) {
    
            dataList.clear();
            for (City city:cityList){
    
                dataList.add(city.getCityName());
            }
            adapter.notifyDataSetChanged();
            mListView.setSelection(0);
            currentLevel = LEVEL_CITY;
        }else {
    
            int provinceCode = selectedProvince.getProvinceCode();
            String address ="http://guolin.tech/api/china/"+provinceCode;
            queryFromServer(address,"city");
        }
    }
    /*
    查询全国所有省 优先从数据可查询 若无再去服务器查询
    query Provinces()方法中首先会将头布局的标题设置成中国,将返回按钮
    隐藏起来,因为省级列表已经不能再返回了。然后调用 LiteRal的查询接口来从数据库中读取省
    级数据,如果读取到了就直接将数据显示到界面上,如果没有读取到组装出一个请求地址,
    然后调用 queryFromServer()方法来从服务器上查询数据。
     */
    private void queryProvince() {
    
          mTitleText.setText("中国");
          //另外还有一点需要注意,在返回按钮的点击事件里,会对当前 List view的列表级别进行判断。
        //如果当前是县级列表,那么就返回到市级列表,如果当前是市级列表,那么就返回到省级表列表。
        //当返回到省级列表时,返回按钮会自动隐藏,从而也就不需要再做进一步的处理了。
          mButton.setVisibility(View.GONE);
          provinceList = DataSupport.findAll(Province.class);
        if (provinceList.size() > 0) {
    
            dataList.clear();
            for (Province province : provinceList) {
    
                dataList.add(province.getProvinceName());
            }
            adapter.notifyDataSetChanged();
            mListView.setSelection(0);
            currentLevel = LEVEL_PROVINCE;
        }else {
    
            String address = "http://guolin.tech/api/china";
            queryFromServer(address, "province");
        }
    }

    /**
     * query Fromserver()方法中会调用 HttpUtil的send0httPrequest()方法来向服务器发送
     * 请求,响应的数据会回调到 onResponse()方法中,然后我们在这里去调用 Utility的
     * handleprovincesresponse()方法来解析和处理服务器返回的数据,并存储到数据库中。接下
     * 来的一步很关键,在解析和处理完数据之后,再次调用了 queryProvinces()方法来重新加
     * 载省级数据,由于 queryProvinces()方法牵扯到了U操作,因此必须要在主线程中调用,这
     * 里借助了 runonuiThread()方法来实现从子线程切换到主线程。现在数据库中已经存在了数据
     * 因此调用 queryProvinces()就会直接将数据显示到界面上了
     * @param address
     * @param type
     */
    private void queryFromServer(String address, final String type) {
    
        showProgressDialog();

        HttpUtil.sendOkHttpRequest(address, new Callback() {
    
            @Override
            public void onFailure(Call call, IOException e) {
    
                    getActivity().runOnUiThread(new Runnable() {
    
                        @Override
                        public void run() {
    
                            closeProgressDialog();
                            Toast.makeText(getContext(), "加载失败", Toast.LENGTH_SHORT).show();
                        }
                    });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
    
                //我们可以使用如下写法来得到返回的具体内容:
                    String responseText = response.body().string();
                    boolean result = false;
                if ("province".equals(type)) {
    
                    result = Utility.handleProvinceResponse(responseText);
                }else if ("city".equals(type)){
    
                    result = Utility.handleCityResponse(responseText,selectedProvince.getId());
                }else if ("county".equals(type)){
    
                    result = Utility.handleCountyResponse(responseText,selectedCity.getId());
                }
                if (result){
    
                    getActivity().runOnUiThread(new Runnable() {
    
                        @Override
                        public void run() {
    
                            closeProgressDialog();
                            if ("province".equals(type)){
    
                                queryProvince();
                            }else if ("city".equals(type)){
    
                                queryCity();
                            }else if ("county".equals(type)){
    
                                queryCounty();
                            }
                        }
                    });
                }
            }
        });

    }

    private void closeProgressDialog() {
    
        if (mProgressDialog != null) {
    
            mProgressDialog.dismiss();
        }
    }

    private void showProgressDialog() {
    
        if (mProgressDialog == null) {
    
            mProgressDialog = new ProgressDialog(getActivity());
            mProgressDialog.setMessage("正在加载");
            mProgressDialog.setCanceledOnTouchOutside(false);
        }
            mProgressDialog.show();
    }

    /*
    查询市内所有区/县
     */
    private void queryCounty() {
    
        mTitleText.setText(selectedCity.getCityName());
        mButton.setVisibility(View.VISIBLE);
        countyList = DataSupport.where("cityid=?",String.valueOf(selectedCity.getId())).find(County.class);
        if (countyList.size()>0) {
    
            dataList.clear();
            for (County county:countyList
                 ) {
    
                dataList.add(county.getCountyName());
            }
            adapter.notifyDataSetChanged();
            mListView.setSelection(0);
            currentLevel =LEVEL_COUNTY;
        }else {
    
            int provinceCode = selectedProvince.getProvinceCode();
            int cityCode = selectedCity.getCityCode();
            String address = "http://guolin.tech/api/china/"+provinceCode +"/"+cityCode;
            queryFromServer(address,"county");
        }
    }

这样我们就把加载全国省市县的功能完成了,可是碎片是不能直接显示在界面上的,因此我们还需要把它添加到活动里才行。修改 activity main.xml中的代码,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/choose_area_fragment"
        android:name="com.wz.myweatherapp.ChooseAreaFragment"
        />

</FrameLayout>

另外,我们刚才在碎片的布局里面已经自定义了一个标题栏,因此就不再需要原生的Action bar了,修改res/ values/ styles.xml中的代码,如下所示
在这里插入图片描述

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

声明所需网络权限

<uses-permission android:name="android.permission.INTERNET"/>

测试

现在可以运行一下程序进行测试了,效果如下。
在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/kilotwo/article/details/108397955

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法