Jetpack Compose重写TopAppBar实现标题多行折叠详解

目录
  • 前言
  • MediumTopAppBar
    • 阅读源码
      • 核心
  • 解决方法
  • 重写TopAppBarLayout
  • 完整代码

前言

想用composes实现类似掘金的文章详细页面的标题栏

上滑隐藏标题后标题栏显示标题

compose.material3下的TopAppBar不能嵌套滚动

MediumTopAppBar

便使用了MediumTopAppBar一开始用着没什么问题,但是标题字数多了,MediumTopAppBar就不支持了,最多就两行,进入源码一看就明白了

@ExperimentalMaterial3Api
@Composable
fun MediumTopAppBar(
   ...
) {
    TwoRowsTopAppBar(
       ...
    )
}

TwoRowsTopAppBar 官方就是告诉你我就两行,要是不服你就自己写,自己写就自己写,当然我才不自己写呢,直接抄,把TwoRowsTopAppBarcopy过来改改就行,开始想着改TextmaxLines就行,后来才发现TwoRowsTopAppBar是用最大heignt限制的

阅读源码

理解源码可以知道MediumTopAppBar布局可以分为两块

上标题栏(TopAppBa) 和下标题(bottomTitle)分别设置了固定高度

布局 高度
上标题栏 122.dp
下标题 64.dp

这个就是TwoRowsTopAppBar命名的TwoRows的原因

高度是固定在我们改不了

核心

首先限制嵌套滑动的Y轴最大的偏移量也就是高度,目的就是仅隐藏底部标题区域并保留顶部标题
手指上滑后计算上滑偏移量

//官方源码
SideEffect {
    if (scrollBehavior?.state?.heightOffsetLimit != pinnedHeightPx - maxHeightPx) {
        scrollBehavior?.state?.heightOffsetLimit = pinnedHeightPx - maxHeightPx
    }
}

接着scrollBehavior.state.collapsedFraction获取折叠高度百分比(0.0表示完全展开,1.0表示完全折叠)

在利用三阶贝塞尔曲线+百分比设置titleText的Alpha值实现滑动渐显效果

最后实现自定义布局,下标题的高度-上滑偏移量实现折叠标题 并且利用Alpha显示上标题

Column {
   //上标题
    TopAppBarLayout(
       ...
    )
    //下标题
    TopAppBarLayout(
      ...
        heightPx = maxHeightPx - pinnedHeightPx + (scrollBehavior?.state?.heightOffset
            ?: 0f)
      ...
    )
}
......
val layoutHeight = heightPx.roundToInt()
layout(constraints.maxWidth, layoutHeight) {
    // Title
    titlePlaceable.placeRelative(...)
}

解决方法

先计算下布局高度

var bottomLayoutViewSize: IntSize by remember { mutableStateOf(IntSize(0,0)) }
val bottomLayoutBox = @Composable {
    Box(
        modifier= Modifier.onSizeChanged { bottomLayoutViewSize = it },
        content = bottomLayout
    )
}

保留上标题的固定高度,动态计算最大高度

LocalDensity.current.run {
    maxHeightPx = 上布局的高度 + 下布局的高度
}

重写TopAppBarLayout

为下布局重写TopAppBarLayout,去除里面的无用代码

使用方法和MediumTopAppBar一样,只不过

title变成了topLayoutbottomLayout两个Composable

为了方便实现不同的字体风格其他布局,可以像掘金一样显示头像关注

KnowledgeTopAppBar(
    topLayout = {
        Text(
            modifier = Modifier.padding(6.dp),
            text = "九狼JIULANG",
            color = CustomTheme.colors.textPrimary,
            fontSize = 21.sp,
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
            fontWeight = FontWeight.Bold
        )
    },
    bottomLayout = {
        Text(
            modifier = Modifier.padding(vertical = 6.dp, horizontal = 12.dp),
            text = "关注 点赞 ",
            color = CustomTheme.colors.textPrimary,
            fontSize = 19.sp,
            fontWeight = FontWeight.Bold
        )
    },
    navigationIcon = {
  },
    actions = {
    },
    scrollBehavior = scrollBehavior
)

完整代码

import androidx.compose.animation.core.*
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.text.TextStyle
import com.jiulang.wordsfairy.ui.theme.CustomTheme
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.roundToInt
import androidx.compose.ui.layout.*
import androidx.compose.ui.unit.*
import com.google.accompanist.insets.statusBarsPadding
@ExperimentalMaterial3Api
@Composable
fun KnowledgeTopAppBar(
    modifier: Modifier = Modifier,
    titleBottomPadding: Dp = 28.dp,
    navigationIcon: @Composable () -> Unit,
    actions: @Composable RowScope.() -> Unit,
    topLayout: @Composable () -> Unit,
    bottomLayout: @Composable BoxScope.() -> Unit,
    pinnedHeight: Dp = 46.0.dp,
    scrollBehavior: TopAppBarScrollBehavior
){
    val pinnedHeightPx: Float
    val maxHeightPx: Float
    val titleBottomPaddingPx: Int
    var bottomLayoutViewSize: IntSize by remember { mutableStateOf(IntSize(0,0)) }
    //计算布局高度
    val bottomLayoutBox = @Composable {
        Box(
            modifier= Modifier.onSizeChanged { bottomLayoutViewSize = it },
            content = bottomLayout
        )
    }
    LocalDensity.current.run {
        pinnedHeightPx = pinnedHeight.toPx()
        maxHeightPx = bottomLayoutViewSize.height.toFloat() +pinnedHeightPx
        titleBottomPaddingPx = titleBottomPadding.roundToPx()
    }
    // 设置应用程序栏的高度偏移限制以仅隐藏底部标题区域并保留顶部标题
    // 折叠时可见。
    SideEffect {
        if (scrollBehavior.state.heightOffsetLimit != pinnedHeightPx - maxHeightPx) {
            scrollBehavior.state.heightOffsetLimit = pinnedHeightPx - maxHeightPx
        }
    }
    val colorTransitionFraction = scrollBehavior.state.collapsedFraction
    val appBarContainerColor by rememberUpdatedState(CustomTheme.colors.statusBarColor)
    val actionsRow = @Composable {
        Row(
            horizontalArrangement = Arrangement.End,
            verticalAlignment = Alignment.CenterVertically,
            content = actions
        )
    }
    val topLayoutAlpha = CubicBezierEasing(.8f, 0f, .8f, .15f).transform(colorTransitionFraction)
    val bottomLayoutAlpha = 1f - colorTransitionFraction
    // Hide the top row title semantics when its alpha value goes below 0.5 threshold.
    // Hide the bottom row title semantics when the top title semantics are active.
    val hideTopRowSemantics = colorTransitionFraction < 0.5f
    val hideBottomRowSemantics = !hideTopRowSemantics
    // Set up support for resizing the top app bar when vertically dragging the bar itself.
    val appBarDragModifier = if (!scrollBehavior.isPinned) {
        Modifier.draggable(
            orientation = Orientation.Vertical,
            state = rememberDraggableState { delta ->
                scrollBehavior.state.heightOffset = scrollBehavior.state.heightOffset + delta
            },
            onDragStopped = { velocity ->
                settleAppBar(
                    scrollBehavior.state,
                    velocity,
                    scrollBehavior.flingAnimationSpec,
                    scrollBehavior.snapAnimationSpec
                )
            }
        )
    } else {
        Modifier
    }
    Surface(modifier = modifier.then(appBarDragModifier), color = appBarContainerColor) {
        Column {
            TopAppBarLayout(
                modifier = Modifier
                    .statusBarsPadding()
                    // 在填充后剪辑,这样不会在插入区域上显示标题
                    .clipToBounds(),
                heightPx = pinnedHeightPx,
                navigationIconContentColor =
                CustomTheme.colors.mainColor,
                actionIconContentColor =
                CustomTheme.colors.mainColor,
                title = topLayout,
                titleTextStyle = TextStyle.Default,
                titleAlpha = topLayoutAlpha,
                titleVerticalArrangement = Arrangement.Center,
                titleHorizontalArrangement = Arrangement.Start,
                titleBottomPadding = 0,
                hideTitleSemantics = hideTopRowSemantics,
                navigationIcon = navigationIcon,
                actions = actionsRow,
            )
            KnowledgeTitleLayout(
                modifier = Modifier.clipToBounds(),
                heightPx =  maxHeightPx - pinnedHeightPx + scrollBehavior.state.heightOffset,
                title = bottomLayoutBox,
                titleTextStyle = TextStyle.Default,
                titleAlpha = bottomLayoutAlpha,
                titleVerticalArrangement = Arrangement.Bottom,
                titleHorizontalArrangement = Arrangement.Start,
                titleBottomPadding = titleBottomPaddingPx,
                hideTitleSemantics = hideBottomRowSemantics,
            )
        }
    }
}
@OptIn(ExperimentalMaterial3Api::class)
private suspend fun settleAppBar(
    state: TopAppBarState,
    velocity: Float,
    flingAnimationSpec: DecayAnimationSpec<Float>?,
    snapAnimationSpec: AnimationSpec<Float>?
): Velocity {
    //检查应用程序栏是否完全折叠/展开。如果是,则无需结算应用程序栏,
    //然后返回零速度。
    //请注意,由于collapsedFraction的浮点精度,不用检查 0f
    if (state.collapsedFraction < 0.01f || state.collapsedFraction == 1f) {
        return Velocity.Zero
    }
    var remainingVelocity = velocity
    //如果有一个初始速度是在前一次用户投掷后留下的,则设置动画以
    // 继续运动以展开或折叠应用程序栏。
    if (flingAnimationSpec != null && abs(velocity) > 1f) {
        var lastValue = 0f
        AnimationState(
            initialValue = 0f,
            initialVelocity = velocity,
        )
            .animateDecay(flingAnimationSpec) {
                val delta = value - lastValue
                val initialHeightOffset = state.heightOffset
                state.heightOffset = initialHeightOffset + delta
                val consumed = abs(initialHeightOffset - state.heightOffset)
                lastValue = value
                remainingVelocity = this.velocity
                // 避免舍入错误,如果有任何内容未被使用,则停止
                if (abs(delta - consumed) > 0.5f) this.cancelAnimation()
            }
    }
    // 如果提供了动画规格,则捕捉。
    if (snapAnimationSpec != null) {
        if (state.heightOffset < 0 &&
            state.heightOffset > state.heightOffsetLimit
        ) {
            AnimationState(initialValue = state.heightOffset).animateTo(
                if (state.collapsedFraction < 0.5f) {
                    0f
                } else {
                    state.heightOffsetLimit
                },
                animationSpec = snapAnimationSpec
            ) { state.heightOffset = value }
        }
    }
    return Velocity(0f, remainingVelocity)
}
@Composable
private fun TopAppBarLayout(
    modifier: Modifier,
    heightPx: Float,
    navigationIconContentColor: Color,
    actionIconContentColor: Color,
    title: @Composable () -> Unit,
    titleTextStyle: TextStyle,
    titleAlpha: Float,
    titleVerticalArrangement: Arrangement.Vertical,
    titleHorizontalArrangement: Arrangement.Horizontal,
    titleBottomPadding: Int,
    hideTitleSemantics: Boolean,
    navigationIcon: @Composable () -> Unit,
    actions: @Composable () -> Unit,
) {
    Layout(
        {
            Box(
                Modifier
                    .layoutId("navigationIcon")
                    .padding(start = TopAppBarHorizontalPadding)
            ) {
                CompositionLocalProvider(
                    LocalContentColor provides navigationIconContentColor,
                    content = navigationIcon
                )
            }
            Box(
                Modifier
                    .layoutId("title")
                    .padding(horizontal = TopAppBarHorizontalPadding)
                    .then(if (hideTitleSemantics) Modifier.clearAndSetSemantics { } else Modifier)
                    .graphicsLayer(alpha = titleAlpha)
            ) {
                ProvideTextStyle(value = titleTextStyle) {
                    CompositionLocalProvider(
                        content = title
                    )
                }
            }
            Box(
                Modifier
                    .layoutId("actionIcons")
                    .padding(end = TopAppBarHorizontalPadding)
            ) {
                CompositionLocalProvider(
                    LocalContentColor provides actionIconContentColor,
                    content = actions
                )
            }
        },
        modifier = modifier
    ) { measurables, constraints ->
        val navigationIconPlaceable =
            measurables.first { it.layoutId == "navigationIcon" }
                .measure(constraints.copy(minWidth = 0))
        val actionIconsPlaceable =
            measurables.first { it.layoutId == "actionIcons" }
                .measure(constraints.copy(minWidth = 0))
        val maxTitleWidth = if (constraints.maxWidth == Constraints.Infinity) {
            constraints.maxWidth
        } else {
            (constraints.maxWidth - navigationIconPlaceable.width - actionIconsPlaceable.width)
                .coerceAtLeast(0)
        }
        val titlePlaceable =
            measurables.first { it.layoutId == "title" }
                .measure(constraints.copy(minWidth = 0, maxWidth = maxTitleWidth))
        // Locate the title's baseline.
        val titleBaseline =
            if (titlePlaceable[LastBaseline] != AlignmentLine.Unspecified) {
                titlePlaceable[LastBaseline]
            } else {
                0
            }
        val layoutHeight = heightPx.roundToInt()
        layout(constraints.maxWidth, layoutHeight) {
            // Navigation icon
            navigationIconPlaceable.placeRelative(
                x = 0,
                y = (layoutHeight - navigationIconPlaceable.height) / 2
            )
            // Title
            titlePlaceable.placeRelative(
                x = when (titleHorizontalArrangement) {
                    Arrangement.Center -> (constraints.maxWidth - titlePlaceable.width) / 2
                    Arrangement.End ->
                        constraints.maxWidth - titlePlaceable.width - actionIconsPlaceable.width
                    // Arrangement.Start.
                    // An TopAppBarTitleInset will make sure the title is offset in case the
                    // navigation icon is missing.
                    else -> max(12.dp.roundToPx(), navigationIconPlaceable.width)
                },
                y = when (titleVerticalArrangement) {
                    Arrangement.Center -> (layoutHeight - titlePlaceable.height) / 2
                    // Apply bottom padding from the title's baseline only when the Arrangement is
                    // "Bottom".
                    Arrangement.Bottom ->
                        if (titleBottomPadding == 0) layoutHeight - titlePlaceable.height
                        else layoutHeight - titlePlaceable.height - max(
                            0,
                            titleBottomPadding - titlePlaceable.height + titleBaseline
                        )
                    // Arrangement.Top
                    else -> 0
                }
            )
            // Action icons
            actionIconsPlaceable.placeRelative(
                x = constraints.maxWidth - actionIconsPlaceable.width,
                y = (layoutHeight - actionIconsPlaceable.height) / 2
            )
        }
    }
}
@Composable
private fun KnowledgeTitleLayout(
    modifier: Modifier,
    heightPx: Float,
    title: @Composable () -> Unit,
    titleTextStyle: TextStyle,
    titleAlpha: Float,
    titleVerticalArrangement: Arrangement.Vertical,
    titleHorizontalArrangement: Arrangement.Horizontal,
    titleBottomPadding: Int,
    hideTitleSemantics: Boolean,
) {
    Layout(
        {
            Box(
                Modifier
                    .layoutId("title")
                    .then(if (hideTitleSemantics) Modifier.clearAndSetSemantics { } else Modifier)
                    .graphicsLayer(alpha = titleAlpha)
            ) {
                ProvideTextStyle(value = titleTextStyle) {
                    CompositionLocalProvider(
                        content = title
                    )
                }
            }
        },
        modifier = modifier
    ) { measurables, constraints ->
        val maxTitleWidth =  constraints.maxWidth
        val titlePlaceable =
            measurables.first { it.layoutId == "title" }
                .measure(constraints.copy(minWidth = 0, maxWidth = maxTitleWidth))
        val layoutHeight =heightPx.roundToInt()
        layout(maxTitleWidth, layoutHeight) {
            // Title
            titlePlaceable.placeRelative(
                x = when (titleHorizontalArrangement) {
                    Arrangement.Center -> (constraints.maxWidth - titlePlaceable.width) / 2
                    Arrangement.End ->
                        constraints.maxWidth - titlePlaceable.width
                    else -> max(0.dp.roundToPx(), 0.dp.roundToPx())
                },
                y = when (titleVerticalArrangement) {
                    Arrangement.Center -> (layoutHeight - titlePlaceable.height) / 2
                    // Apply bottom padding from the title's baseline only when the Arrangement is
                    // "Bottom".
                    Arrangement.Bottom ->
                        if (titleBottomPadding == 0) layoutHeight - titlePlaceable.height
                        else layoutHeight - titlePlaceable.height - max(
                            0,
                            titleBottomPadding - titlePlaceable.height
                        )
                    // Arrangement.Top
                    else -> 0
                }
            )
        }
    }
}
private val TopAppBarHorizontalPadding = 4.dp

以上就是Jetpack Compose重写TopAppBar实现标题多行折叠详解的详细内容,更多关于Jetpack Compose TopAppBar的资料请关注我们其它相关文章!

时间: 2022-11-16

Jetpack&nbsp;Compose自定义动画与Animatable详解

目录 AnimationSpec 1.spring 2.tween 3.keyframes 4.repeatable 5.snap Animatable 本篇主要是自定义动画与Animatable. AnimationSpec 上一篇中,出现了多次animationSpec属性,它是用来自定义动画规范的.例如: fun Modifier.animateContentSize( animationSpec: FiniteAnimationSpec<IntSize> = spring(), fin

Android Jetpack Compose开发实用小技巧

目录 前言 实用小技巧 如何移除View点击阴影 Text文本如何垂直居中 如何移除Button的点击阴影 Dialog宽度如何全屏 如何提升编码效率 前言 在Compose开发的过程中,我们会经常遇到一些看起来很简单却不知道如何处理的小问题,比如去除点击阴影.Dialog全屏等问题,本文记录了这些常见小问题的处理方式.如有更好方案欢迎大佬们交流探讨- 实用小技巧 如何移除View点击阴影 这里的View指的是除了Button系列的之外,如Button.TextButton等,也就是自身没有on

Jetpack&nbsp;Compose&nbsp;双指拖拽实现详解

目录 Modifier.offset graphicsLayer Modifier.pointerInput PointerInputScope.detectTransformGestures 逻辑解释 定义4个变量 传入graphicsLayer里面 监听手势 完整代码 效果图 Modifier.offset Compose遇到一个浏览图片的功能,双指放大和缩小 Modifier的offset可以偏移内容.偏移量可以是正的,也可以是非正的.应用偏移只会更改内容的位置,而不会影响其大小测量. o

Jetpack Compose DropdownMenu手指跟随点击显示

目录 引言 效果图 实现方法 1使用DropdownMenu的offset参数 2Modifier.offset 获取到点击的位置 Box创建用于监听点击事件修饰符 DropdownMenu外层的Box()设置偏移量 完整代码 使用方法 引言 DropdownMenu显示时默认会避开点击的view 通常默认显示在左下方 本篇文章教你实现跟随手指按下位置显示 效果图 实现方法 首先要获取到点击的位置之后计算偏移量 先分析两种offset参数 1使用DropdownMenu的offset参数 获取到

Jetpack Compose惯性衰减动画AnimateDecay详解

目录 什么是惯性衰减动画 惯性衰减动画 使用要点 block 监听 什么是惯性衰减动画 比如说我们玩微信的时候 手指一拉,微信的列表就会惯性滑动 ,这个滑动的速率当然是越来越慢的,最终停止, 这个其实就是惯性衰减动画的典型例子 那这个例子和animateTo 有啥区别呢? 一个速率变慢的动画 ,听起来似乎 我们用animateTo 设置一些参数也可以实现 其实这里最大的区别就是 animateTo 你是需要设置目标值的,也就是动画结束的那一刻 某个view属性的值 你必须明确指定 而所谓的惯性衰

Jetpack Compose 分步指南教程详解

目录 前言 可组合函数 显示简单文本 将样式应用于文本 使用 TextField 进行输入 在 Android Studio 中预览 预览参数 Column Scrollable Column Lazy Column Box Button Card Clickable Image Alert Dialog Material AppBar Material BottomNavigation Material Checkbox Material ProgressBar Material Slider

使用 Docker 搭建 Laravel 本地环境的教程详解

Laravel 官方提供 Homestead 和 Valet 作为本地开发环境,Homestead 是一个官方预封装的 Vagrant Box,也就是一个虚拟机,但是跟 docker 比,它占用体积太大,启动速度慢,同时响应速度很慢,现在有了 docker 这种更好的方式,可以轻松方便的搭建整套 PHP 开发环境. 本文就介绍如何使用 docker 搭建 Laravel 本地环境. 安装 docker 首先安装 docker. 克隆 laradock laradock 官方文档: http://

Spring Boot 集成MyBatis 教程详解

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者. 在集成MyBatis前,我们先配置一个druid数据源. Spring Boot 系列 1.Spring Boot 入门 2.Spring Boot 属性配置

Swift 5.1 之类型转换与模式匹配的教程详解

类型转换在Swift中使用 is 和 as 操作符实现. 类型检查 使用操作符 is 检查一个实例是否是某个确定的类以及其继承体系的父类或子类类型.如果是某个确定的类(该类继承体系的父类或子类)类型,则返回 true ,否则返回 false . class Cat { func hairColor() -> String { return "五颜六色" } } class WhiteCat: Cat { override func hairColor() -> String

Python代码调试技巧教程详解

目录 关于代码调试的技巧,我之前写过很多的文章,加起来也有 将近 10 篇了,关注比较早的同学,也应该都有看过. 还没看过的同学,欢迎前往查阅:调试技巧 其中有一篇是关于 pdb 的调试技巧的: 里面介绍了两种 pdb 的调试入口,也是大部分所熟知的. 这里再带大家回顾一下 第一种:指定 -m pdb 来开启 $ python -m pdb pdb_demo.py 第二种:使用 pdb.set_trace() 在代码中设置断点 import pdb pdb.set_trace() 但其实,pdb

Linux下.tar.xz文件的解压教程详解

前言 对于xz这个压缩相信很多人陌生,但xz是绝大数linux默认就带的一个压缩工具,xz格式比7z还要小. 最近在下载某个源码包的时候遇到的这种压缩格式,乘此机会分享一下xz的压缩与解压方法. 安装 如果系统没有xz命令,需要进行安装,安装方法非常简单, 在centos下,直接运行: yum install xz 也可以使用源码包安装: 先下载该工具源码包http://tukaani.org/xz/ 下载后解压进入该目录运行configure生成makefile文件用-prefix指定安装目录

ABP(现代ASP.NET样板开发框架)系列之二、ABP入门教程详解

ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WEB应用程序框架和项目模板. ABP的官方网站:http://www.aspnetboilerplate.com ABP在Github上的开源项目:https://github.com/aspnetboilerplate ABP 的由来 "DRY--避免重复

windows server2016安装MySQL5.7.19解压缩版教程详解

记录了MySQL 5.7.19 winx64解压缩版安装教程,具体内容如下 系统环境:Win7 x64 软件准备:mysql 5.7.19 winx64 下载网址:https://dev.mysql.com/downloads/mysql/ 配置安装流程 具体安装如下: 1.把 mysql-5.7.19-winx64.zip 压缩文件解压到 C:\MySQL\ 目录下: 2.在 C:\MySQL\ 目录下新建 my.ini 配置文件: 3.用文本编辑器或其他编辑器打开 my.ini 文件,把以下

Mysql免安装版设置密码教程详解

方法1: 用SET PASSWORD命令 MySQL -u root mysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('newpass'); 方法2:用mysqladmin mysqladmin -u root password "newpass" 如果root已经设置过密码,采用如下方法 mysqladmin -u root password oldpass "newpass" 方法3: 用UPDA

CentOS 7 x64下Apache+MySQL(Mariadb)+PHP56的安装教程详解

每次搭建新服务器,都要来来回回把这些包再装一下,来来回回搞了不下20遍了吧,原来都是凭经验,配置过程中重复入坑是难免的,故写此文做个备忘.虽然有像xampp这样的集成包,但是在生产环境的Linux发行版上,还是通过包管理工具安装会放心.这次新买的服务器是CentOS 7(7.2)系统,相关配置也都以此版本为主,为方便操作,直接使用root用户配置. CentOS 7的源比较旧,自带的PHP是PHP 5.4,我们想要的是PHP 5.6,这就需要执行以下命令添加额外的remi源. rpm -ivh

MongoDB windows解压缩版安装教程详解

创建目录如下 将mongodb的压缩包解压到mongodb目录下 mongodata下创建data目录存放数据:创建log目录存放目录 cmd 输入命令: D:\mongo\mongodb\bin\mongod.exe --dbpath "D:\mongo\mongodata\data" --logpath "D:\mongo\mongodata\log\MongoDB.log" --install --serviceName "mongo" -